如何使用Swift在iOS中进行编程式转场

207

我正在创建一个使用Facebook SDK来验证用户的应用程序。我试图将 Facebook 的逻辑整合到一个单独的类中。以下是代码(为简洁起见已剥离):

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???
        }
    }
}

我正在使用上面的类方法来检查会话状态的更改,它运行良好。

Q: 一旦我获得了用户数据,如何从这个自定义类中进行视图间的跳转?

仅仅为了明确,我在 storyboard 上有一个带标识符的 segue,并且我正在尝试找到一种方式从不是视图控制器的类中执行 segue。


2
喜欢 performSegue: 吗? - HAS
是的,但这段代码不在视图控制器中,我该怎么实现呢? - Shlomi Schwartz
那么,在这种情况下,您应该将该工作(segueing)从执行工作的对象委托给视图控制器(通过完成块或委托方法)。 - HAS
获取非空布局异常 - Chris Hayes
12个回答

368
如果你在故事板中的两个视图之间存在一个带有转场标识符的segue,那么你可以使用以下方法通过编程方式调用它:
performSegue(withIdentifier: "mySegueID", sender: nil)

对于旧版本:

performSegueWithIdentifier("mySegueID", sender: nil)

你也可以这样做:

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

或者,如果您在导航控制器中:

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

7
谢谢回复,我如何从不是视图的自定义类中调用performSegueWithIdentifier? - Shlomi Schwartz
2
如果我使用你的第二种方法,如何将数据传递到下一个视图? - iamprem
2
您可以使用 func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 方法并设置属性。 - Lloyd Sargent
1
注意在调用performSequeWithIdentifer()时,将会调用您的调用控制器中prepareForSeque()的实现,您可以在那里进行各种数据传递 - 实际上,在prepareForSeque()中接收到的sender参数只是传递给performSequeWithIdentifier()中的sender参数。 - timbo
1
如果在故事板中不存在segue该怎么办呢?例如,如果您想要创建一个指向ViewController自身的segue呢? - scaryguy
显示剩余8条评论

20
如果您的segue在storyboard中存在,并且在两个视图之间有一个segue标识符,您可以使用编程方式调用它。
self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)

如果您正在使用导航控制器

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

我建议您采用第二种方法,使用导航控制器。


self.performSegue(withIdentifier: "yourIdentifierInStoryboard", sender: self)使用以上代码可在 Xcode 中实现自动转场,只需设置好对应的 storyboard 标识符即可。 - Rajneesh071

18
你可以使用NSNotification。 在你的自定义类中添加一个post方法:
NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

在您的 ViewController 中添加一个观察者:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOFReceivedNotication:", name:"NotificationIdentifier", object: nil)
在您的ViewController中添加功能:
func methodOFReceivedNotication(notification: NSNotification){
    self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)
}

8
请不要滥用通知功能。使用委托并明确视图控制器与您的类之间的连接。由于可能有多个订阅者,并且实例可以随意订阅/取消订阅,因此几乎不可能观察通知的控制流程。 - uliwitness

16

您可以像这样使用segue:

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

    }
}

谢谢回复,不过performSegueWithIdentifier是一个视图控制器方法,而我的代码运行在外部类中。 - Shlomi Schwartz

13

Swift 3 - 同时适用于SpriteKit

您可以使用NSNotification

示例:

1.) 在故事板中创建一个segue并将标识符命名为“segue”

2.) 在您正在从中Segue的ViewController中创建一个函数。

func goToDifferentView() {

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

}

3.) 在您的ViewController的ViewDidLoad()中,您正在从创建观察者进行segue。

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

更新 - 上次我使用此代码时,我不得不将.addObserver的调用更改为以下代码以消除错误。

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

4.) 在您要进行Segue的ViewController或Scene中,无论您想在何处触发segue,请添加Post方法。

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

更新 - 上一次我使用这个时,我不得不将.post调用更改为以下代码以消除错误。

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) 的翻译如下: - Michael Samoylov

5

以上已经有很好的答案了,我想强调在执行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"]
        }

如果您完成了要传递到目标视图控制器的数据,那么请在适当的位置执行segue。 您可以在Storyboard Identity检查器中的Storyboard ID字段中设置“IdentifierOfDestinationVC”。

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

3
你想做的对于单元测试非常重要。基本上你需要在视图控制器中创建一个小的本地函数。给这个函数起任何名字,只需包含performSegueWithIndentifier即可。
func localFunc() {
    println("we asked you to do it")
    performSegueWithIdentifier("doIt", sender: self)
}

接下来,修改你的实用类FBManager,加入一个初始化方法,该方法需要传入一个函数参数和一个变量,用于保存视图控制器中执行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")
    }
}

方便的初始化允许您在不执行转场的情况下在单元测试中使用此功能。 最后,在 //todo: segue to the next view??? 中,放置类似以下的内容:
self.yourFunction()

在您的单元测试中,您可以简单地调用它如下:
let f = UtilClass()
f.doThatThing()

其中,UtilClass 指的是 FBManager,doThatThing 是您的 fbsessionstatechange。

在您的实际代码中,只需将 localFunc(无括号)传递给 FBManager 类即可。


3

这对我很有帮助。

首先,在Identity Inspector中为你的Storyboard中的视图控制器分配一个Storyboard ID。然后使用以下示例代码(确保类、Storyboard名称和Storyboard ID与您使用的相匹配):

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)

更多细节请参见http://sketchytech.blogspot.com/2012/11/instantiate-view-controller-using.html。祝一切顺利!

2

另一个选项是使用模态转场

步骤1:前往故事板,并为视图控制器添加故事板ID。您可以在右侧的“Identity Inspector”中找到更改故事板ID的位置。 我们将其称为ModalViewController

步骤2:打开“发送方”视图控制器(让我们称之为ViewController)并将以下代码添加到其中

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)
    }
  }
}

请注意,我们要打开的视图控制器也被称为ModalViewController

步骤3: 要关闭ModalViewController,请添加以下内容

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

1
你可以使用performSegueWithIdentifier函数来完成这件事。

Screenshot

语法:

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

Example :

 performSegueWithIdentifier("homeScreenVC", sender: nil)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接