Passing an image when taped on uicollectionview to a detail view controller in swift - image

Ive been at this for a while but cant seem to crack it in swift
I want a user to be able to select an image in uicollectionView and for that image to appear in a detailed view controller, i can do this quite easily with a peice of text,
and i can do this when there is a static array of images preloaded. but i cant seem to get anywhere with a collectionview which is loaded with images from a camera.
I understand i need to use
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
}
and this function to isolated selected cell.
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
}
I do have these outlets
#IBOutlet weak var collectionView: UICollectionView!
var images = [UIImage]()
image picker stores all images to this array by
images.insert(newImage, atIndex: 0)
when the array would be passed to the detailviewcontroller, i understand that would have to be copied into another local array and then how would i get the current image that was highlighted to be shown first, perhaps using indexPath.Row
Regards

I'm not using segues, and actually I don't quite understand what your problem is, but I'll try to show you how it could be achieved.
First of all, you have an array with images, so I believe your image should be accessed as images[indexPath.row]
let's suppose that you already have UIViewController class with UIImageView in it.
if so, you can write something like that:
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = MyViewController()
myVC.imageView.image = images[indexPath.row]
self.presentViewController(myVC, animated: true, completion: nil)
}
for modal or
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = MyViewController()
myVC.imageView.image = images[indexPath.row]
self.navigationController?.pushViewController(myVC, animated: true)
}
if you want to show it as navigational.
I believe that with segues it's basically the same but I think you have to use func prepareForSegue for, you know, preparing the segue (func performSegueWithIdentifier will just perform it). In prepareForSegue you should check identifier of your segue
if segue.identifier == "myIdentifier" {
//your code here
}
and if this identifier is right, put your commands to your myVC there.

Related

How to get the right row in a #IBsegueAction function?

I can segue from an embedded UIKit tableview to a SwiftUI view, with the necessary data. I select the indexPath.row with a tableView(_didSelectRowAt).
However, the #IBSegueAction takes place before the didSelectRowAt. This makes the detailView lag one selected row: it shows the previously selected row.
I tried to put the didSelectRowAt first, tried to embed them: no chance
I saw in a WWDC video that it should be possible lo select the right row, but can't figure out the right syntax from this short segment (about minute 6:00)
https://developer.apple.com/videos/play/wwdc2019/231/
#IBSegueAction func MeasurementDetail(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: PointDetailSwiftUIView(pointDetail: measurements[selectedMeasurementRow]))
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedMeasurementRow = indexPath.row
}
How do I solve the problem?
In that WWDC session, Tanu doesn't care when UIKit calls tableView(_:didSelectRowAt:). Instead, her #IBSegueAction asks the table view for the selected row, by using tableView.indexPathForSelectedRow.
Make sure you have a tableView outlet connected to your table view. You already have a tableView outlet if your view controller is a subclass of UITableViewController. Then use tableView.indexPathForSelectedRow:
#IBSegueAction func MeasurementDetail(_ coder: NSCoder) -> UIViewController? {
guard let row = tableView.indexPathForSelectedRow?.row else { return nil }
let detailView = PointDetailSwiftUIView(pointDetail: measurements[row])
return UIHostingController(coder: coder, rootView: detailView)
}

pass UIimage from UITableViewCell to next ViewController [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 3 years ago.
There are many other topics of passing data to another ViewController - i know - but i could not find the solution for passing data from UITableViewCell to UIViewController. The question differs from others that here i have to access an ImageView in another class of UITableViewCell. Segues, prepare for segues an other topics are discussed in other posts sufficiently but not this special constellation.
I have a class UITableViewCell:
class PostCell: UITableViewCell {
...
// networkService is downloading an image
networkService.downloadImage({ (imageData) in
let image = UIImage(data: imageData as Data)
...
// image is set to UIImageView
self.postImageView.image = image
In my ViewController i do this to go to the DetailViewController:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
aktIndex = indexPath.section
performSegue(withIdentifier: "segueDetail", sender: self)
}
I tried this:
let MainStory:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let desVC = MainStory.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
desVC.getImage = ???
self.navigationController?.pushViewController(desVC, animated: true)
DetailViewController:
class DetailViewController: UIViewController {
var getImage = UIImage()
...
I have made a segue in xcode (segueDetail):
At the moment i store the imagedata in UserDefaults and read them again in the DetailViewController. Very weird, i know.
Where do i have to pass the data from? In my PostCell or in the ViewController? The problem is to get access to image-data from PostCell in my ViewController.
In tableView(_:didSelectRowAt:) when you call performSegue(withIdentifier: sender:), you can pass any data or references in it. This is now available in prepare(forSegue:sender:) and your last shot at preparing the data to be passed to the segued viewController.
Example (Using Segue):
If segueDetail is properly hooked up via storyboard and your user taps on a row, you could send the indexPath to the segue like:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "segueDetail", sender: indexPath)true)
}
Then in prepare(forSegue:sender:), depending on your solution, you can prepare access to the required data that you need to pass to the next viewController like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? DetailViewController {
let indexPath = sender as! IndexPath
let cell = tableView.cellForRow(at: indexPath) as! PostCell
vc.getImage = cell.postImageView.image
}
}
Example (Manually without segue):
If you are not using a segue and the user taps on a row, you could manually push a viewController with data like:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
let cell = tableView.cellForRow(at: indexPath) as! PostCell
vc.getImage = cell.postImageView.image
self.navigationController?.pushViewController(desVC, animated: true)
}
And your DetailViewController should be:
class DetailViewController: UIViewController {
var getImage: UIImage?
//...
NOTE: This answer is the best I could fit to work with the given content.
It should just about work but please don't just copy-paste as it's not optimized (for example, your user taps on a cell before it's image is downloaded).
This was just to show the basics, so please improvise and apply proper case handling.

Saving reorder of TableView cells using NSCoding

I have an iOS project I'm working on using Xcode7 and Swift2. I have a TableView that fetches an array from NSCoding. I have it started so the user can reorder the TableViewCells in the TableView. However I need it to save the new order once the user is finished and clicks 'done' which is a UIBarButtonItem. I have a value in my NSCoding object called cellOrder. I looked here and saw this for CoreData. How how would I do this for NSCoding and where do I save it?
I have the following code started for the TableViewCell movement:
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true}
func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
let itemToMove = details[fromIndexPath.row]
details.removeAtIndex(fromIndexPath.row)
details.insert(itemToMove, atIndex: toIndexPath.row)
}
details is the array my data is kept in, using NSCoding.
I decided the best way for me was to save the details array to NSUserDefaults after the the reorder was done. I saw here how to convert the NSObject to NSData to be saved. My code for the moveRowAtIndexPath section is now:
func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
let itemToMove = details[fromIndexPath.row]
details.removeAtIndex(fromIndexPath.row)
details.insert(itemToMove, atIndex: toIndexPath.row)
// Archive NSObject and save as NSData
let myData = NSKeyedArchiver.archivedDataWithRootObject(details)
// NSUserDefaults Save for Reorder of cells
reportDefaults.setObject(myData, forKey: "savedReportListKEY")
reportDefaults.synchronize()
// NSCoding Save
saveReport()
}

didSelectRowAtIndexPath and performSegueWithIdentifier

I have a tableView that display a list of cells. I need to catch the selected cell, and pass my object data based on indexPath.row to display it on my Second View Controller. My didSelectRowAtIndexPath is working fine, but probably I'm not familiar with the syntax for performSegueWithIdentifier, where it fails to call the method.
Sorry I'm new to Swift programming, would appreciate if you could explain more details.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
selectedCell = indexPath.row
performSegueWithIdentifier("DetailScreen", sender: self)
}
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
detailedView.personName = personList[selectedCell].GetPersonName()
}
Solution 1
First, make sure you have segue between your first and second view controllers (From the table view controller one to the Detail view screen). Make sure that the segue has a name
Now in your first view controller's didSelectRowAtIndexPath event, Invoke the segue like this.
performSegueWithIdentifier("DetailScreen", sender: nil)
Now in the prepareForSegue method of the first view controller, you can customize it to send more details.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if(segue.identifier=="DetailScreen")
{
let ctrl = segue.destinationViewController as! YourDetailsViewController
ctrl.personName = "Set some person name here"
}
}
Assuming Your Detail screen's class is called YourDetailsViewController and it has a property called personName of String type. You may update the code to use your real view controller.
Solution 2
If you do not want to create the segue, you can programmatically navigate( actually pushing the second view controller to the front) to the second view. In your first view controllers, didSelectIndexPathAtRow method,
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let ctrl = self.storyboard?.instantiateViewControllerWithIdentifier("DetailsCtrl")
as? YourDetailsViewController
ctrl?.personName="Set some name here"
self.navigationController?.pushViewController(ctrl!, animated: true)
}
For the above code to work, you need to make sure that you have StoryBoardID set to "DetailsCtrl" on the second view controller(Details view)
You have set the storyboard segue as given below:
Click on the segue and add the identifier
You can achieve the above requirement using the following code and this worked for me:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let storyboard1 = UIStoryboard(name: "Main", bundle: nil)
let detail = storyboard1.instantiateViewControllerWithIdentifier("DetailScreen") as! SecondViewController
detail.personName = personList[selectedCell].GetPersonName()
self.navigationController?.pushViewController(detail, animated: true)
}
The storyboard segue from your first view controller to second view controller should be "Push".
Hope this might be helpful.

TableViewCell with UITextView not aligned and cutted until scolling

I've a TableViewCell with a UITextView, which content is not aligned and cutted at bottom at the first display:
When I scroll down and then up to the top, everything is fine:
My cellForRowAtIndexPath to get the content from a fetchedResultsController is simple:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TextViewCell") as! TextViewCell
let data = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
let text = data.valueForKey("textDu")!.description
cell.textContentView.text = text
return cell
}
How can I get the result after scrolling after start???
Use sizeToFit() after adding content to your textContentView.
cell.textContentView.text = text
cell.textContentView.sizeToFit()
Make sure for sizing cell
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "onContentSizeChange:",
name: UIContentSizeCategoryDidChangeNotification,
object: nil)
tableView.estimatedRowHeight = 89
tableView.rowHeight = UITableViewAutomaticDimension
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func onContentSizeChange(notification: NSNotification) {
tableView.reloadData()
}
Hope it helps you.
In conjunction with #Ashish Kakkad's answer you may want to try to set heightDimensions in viewDidLoad or viewWillAppear:
yourTableView.estimatedRowHeight = 30.0 // Put a real estimate here
yourTableView.rowHeight = UITableViewAutomaticDimension
Use auto layout code to tie the bottom of the cells contentView to the bottom of the text box. When the text box resizes it'll expand the cell with it.
This is in addition to Asish's correct suggestion about automatic cell heights and is quite a high level suggestion as you need to do a few things to get auto layout working right in tableview cells but there's ample examples on that out on the web.
oh, oh. Think I found something. I removed the existing contraints and then I've tried to "add missing constaints". The result was thas the error "Failed to automatically update constraints". Seem's I've a problem with my storyboard-file...

Resources