ChatGPT解决这个技术问题 Extra ChatGPT

How to segue programmatically in iOS using Swift

I'm creating an app that uses the Facebook SDK to authenticate users. I'm trying to consolidate the facebook logic in a separate class. Here is the code (stripped for simplicity):

import Foundation

class FBManager {
    class func fbSessionStateChane(fbSession:FBSession!, fbSessionState:FBSessionState, error:NSError?){
        //... handling all session states
        FBRequestConnection.startForMeWithCompletionHandler { (conn: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
        
            println("Logged in user: \n\(result)");
        
            let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
            let loggedInView: UserViewController = storyboard.instantiateViewControllerWithIdentifier("loggedInView") as UserViewController
        
            loggedInView.result = result;
        
            //todo: segue to the next view???
        }
    }
}

I'm using the above class method to check session state changes, and it works fine.

Q: Once I have the user's data, how can I segue to the next view from within this custom class?

Just to be clear, I have a segue with identifier on the storyboard, and I'm trying to find a way to perform a segue from a class which is not the view controller

Like performSegue:?
Yes, but the code is not in the viewController, how can I achive this?
Well, in that case you should delegate that work (the segueing) from the object you do the work in to the view controller (via a completion block or a delegate method).
getting a non nil layout exception

O
Ogre Codes

If your segue exists in the storyboard with a segue identifier between your two views, you can just call it programmatically using:

performSegue(withIdentifier: "mySegueID", sender: nil)

For older versions:

performSegueWithIdentifier("mySegueID", sender: nil)

You could also do:

presentViewController(nextViewController, animated: true, completion: nil)

Or if you are in a Navigation controller:

self.navigationController?.pushViewController(nextViewController, animated: true)

Thanks for the reply, how can I call performSegueWithIdentifier from a custom class which is not the view?
If i use your second method. How to pass data to the next view?
You would use func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) and set the properties.
Note when calling performSequeWithIdentifer(), an implementation of prepareForSeque() in your calling controller will be called and you can do various data passing there - in fact, the sender parameter received in prepareForSeque() is simply what is passed to the sender parameter in performSequeWithIdentifier()
What if the segue does not exist in the storyboard? For example, if you want to create a segue to ViewController itself?
M
Manish Mahajan

If your segue exists in the storyboard with a segue identifier between your two views, you can just call it programmatically using

self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)

If you are in Navigation controller

let viewController = YourViewController(nibName: "YourViewController", bundle: nil)        
self.navigationController?.pushViewController(viewController, animated: true)

I will recommend you for second approach using navigation controller.


self.performSegue(withIdentifier: "yourIdentifierInStoryboard", sender: self)
R
Rodrigo Otero

You can use NSNotification

Add a post method in your custom class:

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Add an observer in your ViewController:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOFReceivedNotication:", name:"NotificationIdentifier", object: nil)

Add function in you ViewController:

func methodOFReceivedNotication(notification: NSNotification){
    self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)
}

Please do not abuse notifications like this. Use a delegate and make the connection between the view controller and your class explicit. It is near-impossible to observe the control flow of a notification, because there could be multiple subscribers, and instances can subscribe/unsubscribe at will.
D
DevB2F

You can use segue like this:

self.performSegueWithIdentifier("push", sender: self)
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if segue.identifier == "push" {

    }
}

Thanks for the reply, however performSegueWithIdentifier is a viewController method, and my code runs in an external class
T
Timmy Sorensen

Swift 3 - Also works with SpriteKit

You can use NSNotification.

Example:

1.) Create a segue in the storyboard and name the identifier "segue"

2.) Create a function in the ViewController you are segueing from.

func goToDifferentView() {

    self.performSegue(withIdentifier: "segue", sender: self)

}

3.) In the ViewDidLoad() of your ViewController you are segueing from create the observer.

NotificationCenter.default.addObserver(self, selector: #selector(goToDifferentView), name: "segue" as NSNotification.Name, object: nil)

Update - Last time I used this I had to change the .addObserver call to the following code to silence the errors.

NotificationCenter.default.addObserver(self, selector: #selector(goToDifferentView), name: NSNotification.Name(rawValue: "segue"), object: nil)

4.) In the ViewController or Scene you are segueing to, add the Post Method wherever you want the segue to be triggered.

NotificationCenter.default.post(name: "segue" as NSNotification.Name, object: nil)

Update - Last time I used this I had to change the .post call to the following code to silence the errors.

NotificationCenter.default.post(NSNotification(name: NSNotification.Name(rawValue: "segue"), object: nil) as Notification)

Swift 3: NotificationCenter.default.addObserver(self, selector: #selector(goToDifferentView), name: NSNotification.Name(rawValue: "segue"), object: nil)
D
Dharman

There are already great answers above, i'd like to put little focus on preparation before performing segue.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.destination is YourDestinationVC {
            let vc = segue.destination as? YourDestinationVC
            // "label" and "friends" are part of destinationVC
            vc?.label = "someText"
            vc?.friends = ["John","Mike","Garry"]
        }

Once your are done with data that you want to pass on to your destinationVC then perform your segue at an appropriate place. You can set "IdentifierOfDestinationVC" in StoryBoard Identity inspector in StoryBoard ID field

performSegue(withIdentifier: "IdentifierOfDestinationVC", sender: nil)

p
pableiros

What you want to do is really important for unit testing. Basically you need to create a small local function in the view controller. Name the function anything, just include the performSegueWithIndentifier.

func localFunc() {
    println("we asked you to do it")
    performSegueWithIdentifier("doIt", sender: self)
}

Next change your utility class FBManager to include an initializer that takes an argument of a function and a variable to hold the ViewController's function that performs the segue.

public class UtilClass {

    var yourFunction : () -> ()

    init (someFunction: () -> ()) {
        self.yourFunction = someFunction
        println("initialized UtilClass")
    }

    public convenience init() {
        func dummyLog () -> () {
            println("no action passed")
        }
        self.init(dummyLog)
    }

    public func doThatThing() -> () {
        // the facebook login function
        println("now execute passed function")
        self.yourFunction()
        println("did that thing")
    }
}

(The convenience init allows you to use this in unit testing without executing the segue.)

Finally, where you have //todo: segue to the next view???, put something along the lines of:

self.yourFunction()

In your unit tests, you can simply invoke it as:

let f = UtilClass()
f.doThatThing()

where doThatThing is your fbsessionstatechange and UtilClass is FBManager.

For your actual code, just pass localFunc (no parenthesis) to the FBManager class.


J
John Moutafis

This worked for me.

First of all give the view controller in your storyboard a Storyboard ID inside the identity inspector. Then use the following example code (ensuring the class, storyboard name and story board ID match those that you are using):

let viewController:
UIViewController = UIStoryboard(
    name: "Main", bundle: nil
).instantiateViewControllerWithIdentifier("ViewController") as UIViewController
// .instantiatViewControllerWithIdentifier() returns AnyObject!
// this must be downcast to utilize it

self.presentViewController(viewController, animated: false, completion: nil)

For more details see http://sketchytech.blogspot.com/2012/11/instantiate-view-controller-using.html best wishes


J
Jayprakash Dubey

You can do this thing using performSegueWithIdentifier function.

https://i.stack.imgur.com/31nGG.png

Syntax :

func performSegueWithIdentifier(identifier: String, sender: AnyObject?)

Example :

 performSegueWithIdentifier("homeScreenVC", sender: nil)

a
aphoe

Another option is to use modal segue

STEP 1: Go to the storyboard, and give the View Controller a Storyboard ID. You can find where to change the storyboard ID in the Identity Inspector on the right. Lets call the storyboard ID ModalViewController

STEP 2: Open up the 'sender' view controller (let's call it ViewController) and add this code to it

public class ViewController {
  override func viewDidLoad() {
    showModalView()
  }

  func showModalView() {
    if let mvc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ModalViewController") as? ModalViewController {
      self.present(mvc, animated: true, completion: nil)
    }
  }
}

Note that the View Controller we want to open is also called ModalViewController

STEP 3: To close ModalViewController, add this to it

public class ModalViewController {
   @IBAction func closeThisViewController(_ sender: Any?) {
      self.presentingViewController?.dismiss(animated: true, completion: nil)
   }
}

佚名

This worked for me:

//Button method example
 @IBAction func LogOutPressed(_ sender: UIBarButtonItem) {

        do {
            try Auth.auth().signOut()
            navigationController?.popToRootViewController(animated: true)

        } catch let signOutError as NSError {
          print ("Error signing out: %@", signOutError)
        }


    }