I have a UIViewController that is set up as a UIPageViewControllerDataSource named MainPVC.swift. It is my entry point on my Storyboard. In it, I am instantiating another UIViewController named TrackerVC which is set up in my Storyboard - but not connected to MainPVC by segue.
Instantiating TrackerVC:
func viewControllerAtIndex(index: Int) -> TrackerVC {
let childViewController = self.storyboard?.instantiateViewControllerWithIdentifier("trackerVC") as TrackerVC
childViewController.screenIndex = index
return childViewController
}
I have another UIViewController named NotesVC. When I push a button on TrackerVC, I use a custom UIStoryBoardSegue to display NotesVC. On NotesVC, I have another button that I want to return to TrackerVC using another custom UIStoryboardSegue via Unwind.
Cancel button tapped in NotesVC:
#IBAction func cancelButtonTapped(sender: AnyObject) {
self.performSegueWithIdentifier("unwindFromNotesSegue", sender: self)
}
Now to my issue -
When I push the button on TrackerVC, the custom UIStoryboardSegue works great and displays NotesVC as expected. However, when I tap the button on NotesVC, the custom UIStoryboardSegue is ignored and the generic segue that slides the view down is being used.
I have traced the issue down to segueForUnwindingToViewController not being called in TrackerVC. I have all of my connections set up properly in my Storyboard and all of the required methods (the method unwind uses).
I have also tried creating a 3rd UIViewController and displaying it using the custom UIStoryboardSegues along with Unwind and calling it from NotesVC and everything works fine. segueForUnwindingToViewController is being called on NotesVC as expected.
Thanks for any help!
Do you have NavigationController attached? You may need to subclass the NavigationController and add the segueForUnwindingToViewController there. It's what solved the similar problem I was having.
Related
I am taking reference from tutorial here.
I have one View Controller which contains a Container View. On this Container View I have added Custom Segue i.e. FirstViewController. That means when I opens View Controller it by default shows FirstViewController. And on FirstViewController, I have a Button. And by clicking on this button I want to show SecondViewController but I am not able to get this achieved. I have also added print command on Button click and it prints on console and not switch to another View. Please help.
I have created a delegate in FirstViewController and aa function which reference through ViewController.
Code for FirstViewController.swift
protocol FirstViewDelegate: class {
func sendToSecondViewController()
}
class FirstViewController: UIViewController {
weak var delegate: FirstViewDelegate? = nil
#IBAction func goToSecond(_ sender: UIButton) {
print("test1")
delegate?.sendToSecondViewController()
}
}
Code for ViewController.swift
extension ViewController: FirstViewDelegate{
func sendToSecondViewController() {
container.segueIdentifierReceivedFromParent("second")
}
}
And main.storyboard
Seems you have not set delegate of FirstViewController in ViewController. Something like:
<object of FirstViewController>.delegate = self (Inside ViewController)
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 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've recently been playing around with swift segues and I'd love to incorporate one in my latest app, the problem is I can't seem to get them to work. So far I've created another view controller SecondViewController and referenced in my ViewController & SecondViewController files as so:
ViewController.swift
import UIKit
class ViewController: UIViewController {
var secondViewController: SecondViewController!
var viewController: ViewController!
override func viewDidLoad() {
//lots more code here
SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
var secondViewController: SecondViewController!
var viewController: ViewController!
override func viewDidLoad() {
Them in storyboard view I've crtl+dragged a segue from viewController to secondViewController and once that's been created given that segue an identifier using the right hand panel, the segue identifier is GameOver and the segue type is show.
Now I want to call the segue automatically with no interaction from the user, in the final app once the user hits the game over func it would trigger the segue and display a new UIView where the highscore could be displayed with a few other items.
The code I'm using to call the segue is:
self.viewController.performSegueWithIdentifier("GameOver", sender: self)
I receive the following error...
Thread 1:EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0
I also have this error in the output field...
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
I've played around with the names of the segues and the file names and I still get the same error, I'm sure I'm missing something fundamental so hopefully someone can help me work this out.
I've created a new project and uploaded it to GitHub, if anyone could tell me what I'm missing that would be great, here is a link to my GitHub repository https://github.com/rich84ts/TestSingleView
Thank you.
You cannot just throw in some instance properties and expect them to magically do something:
class ViewController: UIViewController {
var secondViewController: SecondViewController!
var viewController: ViewController!
}
Those properties are nil, and sending a message to them will crash your app. You have to give them values.
In your case, the segue emanates from this view controller, so what you actually want to say is
self.performSegueWithIdentifier("GameOver", sender: self)
The other big mistake you are making is that you are saying all this in viewDidLoad. That is way too early! You can't do any segue-ing yet; your view is not even in the interface! Move your code into viewDidAppear: and it will actually work:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.performSegueWithIdentifier("GameOver", sender: self)
}
Your code is still silly and useless, but at least you will see something happen and you can continue developing from there.
What I actually recommend is that you delete your viewDidLoad implementation and put this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
delay(1) {
self.performSegueWithIdentifier("GameOver", sender: self)
}
}
That will allow the first view controller to appear, wait one second, then summon the second view controller. And so you will learn that everything is hooked up correctly, and can proceed to do something more practical.
You can create a manual segue from the storyboard by control-clicking the ViewController object and dragging from the manual segue to the destination view controller. You can then call this segue with the designated identifier from your source controller. You don't need a reference to the destination view controller to achieve this.
To reference anything from the storyboard in your view controller you need to declare your properties like this:
#IBOutlet var someProperty : UIView?
The #IBOutlet bit makes the property visible on the storyboard and you can control-drag from it to a corresponding object in a view. You can't do this with view controllers though. To access the destination view controller in your source view controller before the segue you need to override func prepareForSegue(_ segue: UIStoryboardSegue,
sender sender: AnyObject?). This allows you to access the destination view controller from the segue-instance before the actual segue (if you need to pass it data for example).
Firstly your self.viewController is a nil object as you only created the variable and didn't initialize it. You can't call a method with nil object. Secondly you have created a push segue from storyboard but you don't have navigation controller in storyboard so self.performSegueWithIdentifier("GameOver", sender: self) will also not work. To use push segue you should have you current viewcontroller in UINavigationController's stack, so first add a UINavigationController in storyboard and make that initial view controller and set ViewController to the rootViewController of the navigation controller then call self.performSegueWithIdentifier("GameOver", sender: self)
Then the code will work. Hope this help.
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"