How to Push ViewController in UITableViewCell's #IBAction func - swift

I have a UITableViewController and UITableViewCell and Cell's XIB Files.
My question is how to push UIViewController from cell?
in UITableViewCell.
#IBAction func pushBtnTapped(sender: AnyObject) {
// navigate to post view controller
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("GuestCollectionVC") as! GuestCollectionVC
self.navigationController?.pushViewController(guest, animated: true)
}
I got an error message. self.storyboard is not UITableCell's member.
I know there is another way to implement this. But I have to implement in TableViewCell.

Ideally you'd push from the table view controller, in the didSelectRowAtIndexPath method call. Why do you say you have to implement it in the cell? There are other ways you could do that but from what you're saying this might be the best shot...
self.performSegueWithIdentifier("YourSegueId", sender: nil)
Then in prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//get the index path of the selected row
let indexPath = self.tableView.indexPathForSelectedRow
//just an example if you want to pass something to the other vc
let item = self.responseItems[indexPath!.row]
if segue.identifier == "YourSegueId" {
YourVC = segue.destinationViewController as! YourVC
YourVC.item = item
}
}
If you want to do it from the button you could do something like this in the prepareForSegue method... now you have the button and its index path
let button = sender as! UIButton
let view = button.superview
let cell = view?.superview as! YourCell
let indexPath = tableView.indexPathForCell(cell)

Related

Perform a segue programmatically

Hi I'm trying to perform a segue programmatically without the Storyboard. Currently I have this as my code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "popOutNoteExpanded" {
let vc = segue.destination as! ExpandedCellViewController
let cell = sender as! AnnotatedPhotoCell
sourceCell = cell
vc.picture = cell.imageView.image
print("button pressed")
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: "popOutNoteExpanded", sender: indexPath)
}
When I click on the collection view cell it's saying that:
has no segue with identifier 'popOutNoteExpanded'.
Not sure how to perform my custom animated transition.
To trigger a segue programmatically, you have to follow steps:
1. Set it up in Storyboard by dragging your desired segue between two view controllers and set its identifier (for example, in your case is "popOutNoteExpanded") in Attributes Inspector section.
2. Call it programmatically
performSegue(withIdentifier: "popOutNoteExpanded", sender: cell)
Please check if you set its identifier correctly or not.
Besides, in your above code, you put an incorrect sender.
In your prepare() method, you use the sender as a UITableViewCell, but you call performSegue() with sender as a IndexPath.
You need a cell by calling:
let cell = tableView.cellForRow(at: indexPath)
And then you can perform a segue:
performSegue(withIdentifier: "popOutNoteExpanded", sender: cell)
Segues are components of storyboard. If you don't have a storyboard, you can't perform segues. But you can use present view controller like this:
let vc = ViewController() //your view controller
self.present(vc, animated: true, completion: nil)
Additional to the correct answer, please be aware that if you are in a navigation stack (say, you are in a Settings page and want to segue to an MyAccountVC) you should use:
let vc = MyAccountVC()
self.navigationController?.pushViewController(vc, animated: true)
If the navigated VC appears with a transparent background during the transition, just set the backgoundColor of it to .white.
In my case I was presenting a TableViewController, without a segue defined, and found it was necessary to instantiate from the storyboard otherwise tableViewCells couldn't be dequeued in cellForRowAt.
let vc = (storyboard.instantiateViewController(withIdentifier: "YourTableViewController") as? YourTableViewController)!
self.navigationController!.pushViewController( vc, animated: true)

Why is my data not passing to my second view controller from a table view?

I'm trying to pass the value to a second view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "headToDetail" {
let indexPath = self.tableView.indexPathForSelectedRow!.row
print("SELECTED INDEX \(indexPath)")
let destVC = segue.destinationViewController as! SecondViewController
print("Segue TEst \(self.toPass)")
destVC.vieSegue = self.toPass
}
I've remove the didSelectRowAtIndexPath and just going to use the Segue but when I tap on a row cell I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
This is a major update to the original code.
the problem is, that you are creating a new ViewController at line
var destination = SecondViewController()
but then the controller simply deallocates because nothing is pointing to it (there is no reference to it and it falls out of scope). You haven't pushed into it, nor you performed any segue.
How to fix it? Basically there are two options.
Storyboard
If you're working with storyboards, you have to create a segue between the first ViewController and SecondViewController. You have to give it an identifier.
Then in didSelectRowAtIndexPath you will write
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
self.toPass = fruits[row]
performSegueWithIdentifier("YOUR_SEGUE_IDENTIFIER", sender: nil)
}
then in prepareForSegue you'll set the property to the destination SecondViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YOUR_SEGUE_IDENTIFIER" {
let destVC = segue.destinationViewController as! SecondViewController
destVC.vieSegue = self.toPass!
}
}
Manual Push
The second option is to push the controller manually within the didSelectRowAtIndexPath method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
let destination = SecondViewController()
destination.vieSegue = fruits[row]
// if you are using navigation controller
navigationController?.pushViewController(destination, animated: true)
// if you want to present it modally
// presentViewController(destination, animated: true, completion: nil)
}
The second version doesn't force you to create segues.
It's empty because you create a new object...
var destination = SecondViewController()
...pass it your data...
destination.vieSegue = self.toPass!
...and then let it go out of scope without showing it, saving it, or doing anything else useful.

Resign First Responder On Push To Detail

I was wondering how to resignFirstResponder whenever I switch to my detailView in the tableView, is this something I can implement in my prepareForSegue or somewhere else.
Here is my prepareForSegue code.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "DetailView") {
let VC = segue.destinationViewController as! DetailViewLemon
if let indexPath = self.tableView.indexPathForSelectedRow {
let Make = self.resultSearchController.active ?
filteredTableData[indexPath.row] as String :
tableData[indexPath.row] as String
VC.sentData1 = Make
}
}
}
Any UIView (and UIWindow as well, as it is UIView subclass) has method
func endEditing(force: Bool)
This will resign any current first responder descendant in the hierarchy of the view on which you call this method.
So you can call it on the view of the previous detail view controller or at the whole application's window from the prepareForSegue or any other logic point of your program.

I want to send data when I tap a button in tableView Cell

I am implementing a commentView for my app. I have a main view which is tableview contains picture and a button to go comment view.
I want that when user tap comment button in table view, view shows comment view and pass PFObject by prepareforSegue method.
now comment button works, but I have an error from prepareforsegue
here is my code.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "mainToComment") {
let destViewController : CommentVC = segue.destinationViewController as! CommentVC
destViewController.parentObjectID = parentObjectID
let selectedRowIndex = self.tableView.indexPathForSelectedRow
destViewController.object = (postsArray[(selectedRowIndex?.row)!] as? PFObject)
and here is my how my button works.
#IBAction func commentButtonTapped(sender: AnyObject) {
let button = sender as! UIButton
let view = button.superview!
let cell = view.superview as! MainTVCE
let indexPath = tableView.indexPathForCell(cell)
parentObjectID = postsArray[(indexPath?.row)!].objectId!!
when I debug, selectedRowIndex has no value(nil)
I think it cause of I tap button instead of cell.
How can I set indexPath for this?
or
How can I make it work?
I don't know name of your main TableViewCell view controller. Assume that, I name this view controller is MainTableViewCell.
I create a closure in MainTableViewCell:
var didRequestToShowComment:((cell:UITableViewCell) -> ())?
When button comment is tapped:
#IBAction func commentButtonTapped(sender: AnyObject) {
self.didRequestToShowComment?(self) // self is this UITableViewCell
}
In table cellForRowAtIndex... of your main view controller.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
mainTableViewCell.didRequestToShowComment = { (cell) in
let indexPath = tableView.indexPathForCell(cell)
let objectToSend = postsArray[indexPath.row] as? PFObject
// Show your Comment view controller here, and set object to send here
}
...
return cell
}

prepareForSegue collectionView indexPath using swift

I don't find any good example to do what I want using swift so I allow myself to ask the question.
Im using a collectionView to display PFObjects and I want to send the displayed cell data to a second controller using prepareForSegue.
At this point, Im struggling to make this part of the code works:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "goto_answerquestion"){
var indexpath : NSIndexPath = self.collectionView.indexPathsForSelectedItems()
}
}
this line:
var indexpath : NSIndexPath = self.collectionView.indexPathsForSelectedItems()
triggers the following error:
(UICollectionView, numberOfItemsInSection: Int)-> Int does not have a member named 'indexPathsForSelectedItems'
please let me know if Im using the wrong method, or if you need additional data to have the appropriate overview of the problem.
ANSWER
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segue_identifier"){
// check for / catch all visible cell(s)
for item in self.collectionView!.visibleCells() as [UICollectionViewCell] {
var indexpath : NSIndexPath = self.collectionView.indexPathForCell(item as CollectionViewCell)!
var cell : CollectionViewCell = self.collectionView!.cellForItemAtIndexPath(indexpath) as CollectionViewCell
// Grab related PFObject
var objectData:PFObject = self.questionData.objectAtIndex(indexpath.row) as PFObject
// Pass PFObject to second ViewController
let theDestination = (segue.destinationViewController as answerPageViewController)
theDestination.questionObject = objectData
}
}
}
If you just are trying to just find the index path of the cell tapped, and not have multiple, you could do this in your prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = collectionView.indexPathForCell(sender as UICollectionViewCell)
// do what you need now with the indexPath
}
sender in this situation is the cell you tapped, so you just need to cast it to UICollectionViewCell (or a custom cell subclass, if you made one).
UPDATE:
Swift 1.2 introduced as! to replace as for this purpose, so to keep with safety, you can try this inside prepareForSegue using multiple bindings:
if let cell = sender as? UICollectionViewCell, indexPath = collectionView.indexPathForCell(cell) {
// use indexPath
}
This may Solve your problem :
var indexPath : NSArray = self.collectionView.indexPathsForSelectedItems()