I have a navigation controller, with a table view. When I press a cell, the detail view controller opens.
In my root view controller I have :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailview" {
var destination:DetailViewController = segue.destinationViewController as DetailViewController
destination.delegate=self
}
}
In my detail view controller I have a back button :
#IBAction func back() {
self.navigationController?.popViewControllerAnimated(true)
}
The issue is, after 2 go to and return, my app crashes when I go back on the root view controller pressing back button. The console doesn't give me errors. It just crashes.
I think I have forgotten to unwind the segue.
So in my detail view controller I added :
#IBAction func unwindToViewController(segue: UIStoryboardSegue) {
println("unwind function")
}
I connect this function to my back button with "exit" in my storyboard.
When I run my app, If I press on the back button, the console doesn't display my print "unwind function", so unwindToViewController isn't called. Why ?
And my app still crashes...
Your unwindToViewController method should be placed in your root viewController, then ctrl-drag from the button in the detailViewController to the Exit icon in InterfaceBuilder. Choose that method in the popup menu.
Another approach would be to declare a protocol with a function in the rootViewController that is called from the detailViewController. You already set the rootViewController as the delegate of the detailViewController. Within that function you call dismissViewController.
Swift answer...
I had a similar problem.
The func: "segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue" was never called.
Solution:
since I didn't have a "UINavigationController", because I simply embeded the app in a Navigation Controller, I created a UINavigationController subclass for the Navigation Controller and added the function named above on it. Now the app calls "segueForUnwindingToViewController"
Related
I am working in storyboard and also programmatically do some things. First, I have created a viewController controller which is login page (first view) programmatically. But in storyboard I have a NavigationController whose root is ViewController. Everything (methods forgotPassword and loginDidFinish) worked fine, except that ViewController was viewed before controller immediately after launching the app.
So I have changed the root of NavigationController to controller, and after that my functions does not work. I've tried several things like deleting navcontrol in storyboard, etc. You can see my project here: https://github.com/ardulat/SPE
I will provide you a basic example of a Login scenario, hope it can help you with your issue, what I would do first is set right the navigation between ViewControllers like this:
I have two view controllers in my project (LoginViewController.swift and MainViewController.swift):
So in my storyboard I create two ViewControllers, the first one embedded with NavigationController then I set a segue from my first ViewController to my second ViewController:
Then I give a name to the segue I created like so:
And in order to navigate from Login to Main ViewController, I call the performSegue method inside the loginButtonTapped action that is triggered when the login button is touched.
LoginViewController.swift:
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginButtonTapped(_ sender: AnyObject) {
// TODO: validate your login form
performSegue(withIdentifier: "LoginToMain", sender: nil)
}
}
I have this situation :
I have a first view controller , when tap on button in it I open in modal mode another view controller , in this view controller when I tap another button I open in modal view another view controller and in it there is a button and when I tap on it I want to go to first view controller without re-initialize it.
How do I do it?
This is the perfect situation for an unwind segue.
Put this in your first viewController (the one you want to return to):
#IBAction func backFromVC3(_ segue: UIStoryboardSegue) {
print("We are back in VC1!")
}
Then in the Storyboard in your 3rd viewController, control-drag from your button to the exit icon at the top of the viewController and choose backFromVC3 from the pop-up.
Now, when the user presses the button in VC3, both VC3 and VC2 will be dismissed and you will return to VC1.
If you are not using Storyboards, you can dismiss the viewControllers with code. Here is code for a button's handler to dismiss two levels of viewController:
func doDismiss(_ sender: UIButton) {
// Use presentingViewController twice to go back two levels and call
// dismissViewController to dismiss both viewControllers.
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
}
Thanks all for reply and edited my question :)
I found 2 line code to resolved my problem:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window!.rootViewController?.dismissViewControllerAnimated(true, completion: nil).
And that work well.
Thanks very much
I have a TableViewController and I would like to trigger a segue within its navigation bar. I created the segue in the storyboard to my new ViewController. However if I click the bar button item, the view does not appear.
Instead the bar button item becomes inactive (greyed out) and the app freezes. There is no error message and the app does also not crash. The prepareForSegue method in my TableViewController also gets called
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
print("prepare for segue called")
print("destination view controller = \(segue.destinationViewController.description)")
}
I did the following things:
created a custom view Controller class for the second screen (in my storyboard and as a .swift file). I assigned the respective ViewController in the storyboard to my custom view controller in the Identity inspector
created an IBAction for a click event on the button and triggered
the segue programatically. The result remains the same.
prepareForSegue is called. The destionationViewController is correct
but does not show up. I removed this IBAction afterwards.
My destination view controller looks like this
class EnterUserDataViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("EnterUserDataViewController viewDidLoad called")
}
}
viewDidLoad never gets called even though the right segue has been triggered.
Can someone please give me a hint on why this happens?
You wouldn't happen to have a rogue breakpoint set somewhere would you?
If I put a breakpoint somewhere in the view loading cycle it recreates the exact symptoms you are describing.
I have two UICollectionViewControllers and the first one uses a push segue to get to the second one. The problem I'm having is passing information back to the first controller when the back button (the one that gets added automagically) is pressed in the second controller. I've tried using the segueForUnwindingToViewController, and canPerformUnwindSegueAction override functions, but no dice. I need to be able to access both view controllers so I can set some variables. Any ideas?
Here is an example with two view controllers. Let's say that the names of the two view controllers and ViewController and SecondViewController. Let's also say that there is an unwind segue from the SecondViewController to the ViewController. We will pass data from the SecondViewController to the ViewController. First, let's set the identifier of this segue by opening the document outline and selecting the unwind segue. Then open up the attributes inspector and set the identifier to "unwind".
SecondViewController Code:
class SecondViewController: UIViewController
{
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if let destination = segue.destinationViewController as? ViewController {
if identifier == "unwind" {
destination.string = "We Just Passed Data"
}
}
}
}
}
ViewController Code:
class ViewController: UIViewController {
var string = "The String That Will Be We Just Passed Data"
#IBAction func unwindSegue(segue: UIStoryBoardSegue) {
}
}
It sounds like you are trying to intercept the back button, there are many posts for this on SO, here are two:
Setting action for back button in navigation controller
Trying to handle "back" navigation button action in iOS
In practice, it is more clear to return state in closures (more modern), or delegates.
I have an iOS app consists of three view controllers
homeViewController -> newGameSelectionViewController -> GameViewController
In GameViewController, I have a "home" button that should close the view controller and return to the newGameSelectionViewController.
In newGameSelectionViewController,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
gameVC = segue.destinationViewController as! GameViewController
gameVC.countdown = 60
}
Then, I created an unwind segue to newGameSelectionViewController from GameViewController.
In newGameSelectionViewController,
#IBAction func unwindFromHomeButton(segue: UIStoryboardSegue) {
gameVC.dismissViewControllerAnimated(true, completion: nil)
}
When I press the home button in GameViewController, view closes and returns to the newGameSelectionViewController
The problem is, after pressing the home button and returning to the newGameSelectionViewController, countdown timer in GameView controller still continues. If I open an another GameViewController, it also open a completely new view controller. In another words, first GameViewController did not close by calling dismissViewController(). How do I close the ViewController completely so that if I start a new game, previous view controller does not continue counting at the background.
I am printing the countdown values with println() in GameViewController. Thats where I see the previous countdown values are still printing after dismissViewController()
Your gameVC variable will be holding a strong reference to the GameViewController instance, so even though it has been dismissed, the object still exists as its reference count is not 0.
You need to set gameVC to nil in your unwind method in order to remove this reference.
#IBAction func unwindFromHomeButton(segue: UIStoryboardSegue) {
gameVC=nil;
}
You don't need to dismiss the view controller explicitly in the unwind method if you are correctly invoking it via a segue - The unwind process will do this for you.