I’m making an application on UITableView and I want to make a function, when I click on a cell, I open another view controller, on which there will be a back button, I want that when I click on this button, the newly opened controller closes, and returns me to that the controller from which it was opened, how to do it?
This code is for creating a new controller when clicking on a cell (without the function to open the controller, because I don’t know yet with which method I can implement this)
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = createVC(rootVC: AboutRecipeViewController(), TBImageName: nil, TBTitle: recipes[indexPath.row].recipeName)
}
The new ViewController got a method for that.
dismiss(animated: true, completion: nil)
Create an IBAction for your Back Button and run this method when the button got pressed. That should dismiss the new ViewController and get you back to the initial one.
However, your syntax with the back button seems to be similar to a UINavigationViewController. Consider using this makes it way more easy.
Related
Swift 5, Xcode 10
My app uses two ViewControllers:
VC1, UITableView, clicking on a cell calls
VC2, displays further information about a cell with the default UINavigationBarItem "back" button on the top left and an addition "save" button at the bottom
The "save" buttons saves the changes made. To go back to the VC2, you can either click on the "back" button or save the changes, which also automatically loads VC2 using:
#IBAction func onClickSave(_ sender: Any) {
//Save changes
delegate?.passRowSavedBack(true) //Tell VC1 that changes were saved
navigationController?.popViewController(animated: true)
}
If I go back using the UINavigationBarItem "back" button, the cell also keeps its selection color for a second, then removes it (like in the iOS "Contacts" app) using this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
myTableView.deselectRow(at: mylastRowPickedIndex!, animated: true)
}
Example gif.
The problem is: If I go back with the "Save" button, this code is called too but there's no animation.
My guess is that popViewController takes longer to go back than whatever the "back" button calls, so the animation is played but you don't "arrive" in time to see it.
But how do I fix this? Is there a different way to go back to VC1 through the "Save" button (without removing the default "back" button!) that still plays the animation?
Edit: What I'm doing exactly:
In VC2: Save the changes
Tell VC1 that they were saved with a delegate (check 1st code above):
protocol PassingProtocol {
func passRowSavedBack(_ valueSent: Bool)
}
Go back to VC1 using popViewController (check 1st code above)
In VC1: deselectRow in viewWillAppear (check 2nd code above)
rowsSaved[lastRowPicked] = true, so it can add an accessoryType in here:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.accessoryType = (rowsSaved[indexPath.row]==true) ? .checkmark : .none
return cell
}
myTableView.reloadData() - so it displays the new checkmark
Okay, finally I think I understand what is the problem.
You are calling myTableView.reloadData() and this is the problem with deselecting animation.
You can move myTableView.reloadData() in viewDidAppear() and everything will be okay.
I currently have a ViewController with prototype cells in a UITable View. The cells currently display content from a Firebase DB when loaded. What I would like to do is when a cell is pressed more information is shown from the Firebase DB. However, I currently cannot get the segue to push to the ViewController from the cell. What should I do so this would work?
Image of my Storyboard--
These are two pieces of code that will help. For swift:
#IBAction func showEntries(_ sender: Any) {
self.performSegue(withIdentifier: <sequeId>, sender: nil)
}
and a button with the action wrapping this.
For Objective C, you can perform the segue like so.
[self performSegueWithIdentifier:#"showEditor" sender:self];
Create the action by CTRL and dragging the button or cell into the corresponding code file, select action and name it.
It's hard to tell, but from the screenshot, it looks like the segue goes from controller to controller, rather than cell to controller. If you'd like to do it like this, then you need to give the segue an identifier (in the right panel of your screenshot) and then override the delegate method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "yourIdentifier", sender: self)
}
OR (if you don't feel like going to all this trouble)...in the storyboard, drag the segue from the cell to the view controller you want to push, rather than from controller to controller.
For example,
Notice how when the segue is selected, only the cell is outlined in blue, rather than the entire view controller.
It's because cells are dynamic, so you can't assign an event handler from cell to uiviewcontroller. Instead you should make the segue inside your cellForRowAtIndexPath method and push it from there. If you want to see the segue inside the storyboard nevertheless, then you can also ctrl+drag from viewcontroller1 to viewcontroller2 to make a new segue. Then select the segue and give it a Storyboard id. After That you can call self.performSegue(withIdentifier: "yourIdentifier")
I actually found the solution to this. The issue was fairly simple actually. All I had to so was change the selection setting within the table view attributes from 'No Selection' to 'Single Selection'.
I want to create the following user flow in my Swift application:
User clicks a button
A modal view pops over with a choice. The user selects a choice.
A second view appears with another choice (using a "show" transition, so the user can press < Back to alter choice #1). The user selects the second choice.
The second modal view segues back to the original view (which has the button).
To do this, I created the following structure:
--> NC1 --> VC1 --modal-> NC2 --> VC2 --show-> VC3 (NC = UINavigationController,
^ | VC = UIViewController)
| |
----Segue I'm trying to achieve-----
I've tried to pop all the way back to VC1 by passing a reference to VC1 all the way through to VC3, then calling the following code in VC3:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Perform Segue
print("Trying to segue")
rootController!.navigationController!.popToRootViewControllerAnimated(true)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
where rootController is the reference to VC1. Unfortunately the segue doesn't occur; the second modal view stays on the screen, but I get the "Trying to segue" message printed.
Am I trying to implement this user flow the right way? If so, any suggestions why I can't pop to a root view in another navigation controller?
If you are using storyboards, you can do this using an unwind segue.
In VC1 implement an #IBAction method, which takes a UIStoryboardSegue as parameter:
#IBAction func unwindToVC1(sender: UIStoryboardSegue) {
}
Then from the Prototype cell of VC3 control-drag to the Exit icon of the view controller, select Selection Segue: unwindToVC1: and you are good to go.
You can achieve a similar result using code also.
Implement didSelectRowAtIndexPath in VC3 like this:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
navigationController!.dismissViewControllerAnimated(true, completion: nil)
}
Im pretty new to ios development and am starting with a viewcontroller that displays a table of data. When I click on one of the cells, I try to push a tabbarcontroller into view. It works, but I can't see the tabs and the view area is black instead of displaying either of the two default views associated with the tabbarcontroller.
If I set the tabbarcontroller as the intialviewcontroller, it works fine, but this breaks my design.
Any help appreciated.
UDPATE: this is how I'm pusing the tabbarcontroller into view
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let vc = TrialSiteViewController()
navigationController?.pushViewController(vc, animated: true)
}
I tried about four different ways to transition from my tableviewcontroller to my tabbarcontroller and the only way I could get this to work was to use a segue setup in my storyboard with an identifier, then call this when I click the cell.
performSegueWithIdentifier("trialSitesSegue", sender: self)
Probably obvious to a lot of IOS developers, but I am new to all this, so still thought I would share.
I'm learning about iOS as I write an application that uses a UITableViewController and Core Data to display a list of items, and another view controller that also uses Core Data to configure a variety of additional attributes that are not shown in the table view.
A button in the UITableViewController's navigation bar segues to another view that lets me set a variety of details. This is done using a storyboard "Show Detail" segue, and it works fine.
I want to use the accessory action button in a UITableViewCell to segue to the same detail view controller, so I created another "Show Detail" segue in the storyboard that connects the accessory detail button to the detail view controller (ie: the same kind of segue as the button in the navigation bar)
Confusingly, when I click the detail accessory button at run time, nothing happens, and prepareForSegue is never called.
The UITableView cell style is Subtitle, and I've configured it to have a Detail accessory action. However, the accessory button isn't displayed unless I specifically add it:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: Storyboard.reuseID)
let tone = toneTable[indexPath.row]
cell.textLabel!.text = tone.valueForKey("toneName") as? String
cell.detailTextLabel!.text = tone.valueForKey("toneDescription") as? String
cell.accessoryType = UITableViewCellAccessoryType.DetailButton
return cell
I finally gave up, and called prepareForSeque myself:
override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) {
let tone = toneTable[indexPath.row]
self.performSegueWithIdentifier("ShowToneDetail", sender: tone)
}
(I'm using the sender parameter as a convenient place to pass an NSManagedOject)
Now, prepareForSegue gets called, but if I put a breakpoint in prepareForSegue, I see that segue.destinationViewController points to the the source view controller, not the destination view controller. This is unfortunate, because I'd like to pass some values to the new instance of the destinationViewController.
Interestingly, the segue does correctly instantiate the destinationViewController, and passes control to it.
But, I don't understand what's going on. I don't know why I have to call prepareForSeque myself, and I don't know why the segue's destinationViewController isn't set correctly.
What am I doing wrong?
ps: is it just my perception, or is XCode less mature than other IDEs like Eclipse?
You're creating your cells in the wrong way. You should be using dequeueReusableCellWithIdentifier:forIndexPath: to dequeue a cell using the identifier you setup in the storyboard. By doing it the way you are, you're directly instantiating a cell that "knows" nothing about the cell in the storyboard where you added the detail disclosure button, or the segue you made.
I have no idea why the segue.destinationViewController would point to the source view controller. My guess is that it doesn't, and maybe you have your controller classes mixed up.