Swift中的performSegueWithIdentifier不起作用

40

我试图在用户成功登录到他们的帐户后切换视图控制器,但它没有正常工作。我无法直接使用segue,因为如果点击登录按钮,它将无论信息是否正确都会转到那个视图控制器。我已经尝试了我所知道的一切,但没有成功。这是我正在尝试的代码。

   @IBAction func loginTapped(sender: AnyObject) {

    let username = usernameField.text
    let password = passwordField.text

    if username.isEmpty || password.isEmpty {
        var emptyFieldsError:UIAlertView = UIAlertView(title: "Please try again", message: "Please fill in all the fields we can get you logged in to your account.", delegate: self, cancelButtonTitle: "Try again")
        emptyFieldsError.show()
    }

    PFUser.logInWithUsernameInBackground(username, password:password) {
        (user: PFUser?, error: NSError?) -> Void in
        if user != nil {
            self.performSegueWithIdentifier("Klikur", sender: self)
        } else {
            if let errorString = error!.userInfo?["error"] as? String {
                self.errorMessage = errorString
            }

            self.alertView("Please try again", message: "The username password combiation you have given us does not match our records, please try again.", buttonName: "Try again")
        }
    }

}
我将Storyboard的ID设置为“Test”,但在输入正确信息时它没有切换视图控制器。能否有人帮我解决这个问题?

这是LoginViewController的代码 这是KlikurTableViewController的属性面板


移除Segue,然后在按钮点击事件中执行导航。 - Yogesh Suthar
那段代码在函数buttonTapped中,所以它应该进行转换,但是它没有。 - Trenton Tyler
你从故事板中移除了segue吗? - Yogesh Suthar
是的,登录页面和成功登录页面之间没有连线。 - Trenton Tyler
哈哈,我通过调试找到了我的问题,方法是查看 Segues:po self.perform(NSSelectorFromString("storyboardSegueTemplates"))。另外,我发现问题所在,是因为之前的 UIViewController 是通过代码添加到 UINavigationStack 中的,而不是通过 performSegue 添加的,所以没有 Segues。我通过为之前的控制器执行 performSegue 来解决这个问题 :) - iTux
8个回答

75
假设您的代码没有崩溃,而是无法进行segue转场,至少一个问题是:
self.performSegueWithIdentifier("Test", sender: self)

应该是:

dispatch_async(dispatch_get_main_queue()) {
    [unowned self] in
    self.performSegueWithIdentifier("Test", sender: self)
}

请记住,所有UI操作必须在主线程队列上执行。你可以通过检查以下内容来证明自己是否在错误的线程上:

NSThread.isMainThread() // is going to be false in the PF completion handler

补充说明

如果有可能 self 变成 nil,例如因为被关闭或者其他不再需要时被释放,你应该将 self 弱引用为 [weak self] 而不是 unowned,并使用安全的解包方式: if let s = self { s.doStuff() } 或可选链式调用: self?.doStuff(...)

补充说明2

这似乎是一个受欢迎的答案,所以在这里提到这种新的选择很重要:

NSOperationQueue.mainQueue().addOperationWithBlock {
     [weak self] in
     self?.performSegueWithIdentifier("Test", sender: self)
}

注意,来自https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift:

NSOperation与Grand Central Dispatch(GCD)的比较:

GCD [dispatch_* calls]是一种轻量级的表示将会并发执行的工作单元的方式。

与GCD相比,NSOperation增加了一点额外开销,但您可以在各个操作之间添加依赖关系,并可重复使用、取消或暂停它们。

补充3

Apple在此处隐藏了单线程规则:

注意

大多数情况下,请仅从应用程序的主线程使用UIKit类。 对于派生自UIResponder或以任何方式涉及操作应用程序用户界面的类,尤其如此。

SWIFT 4

DispatchQueue.main.async(){
   self.performSegue(withIdentifier: "Test", sender: self)
}

参考:

https://developer.apple.com/documentation/uikit


除非您在此处完全重现崩溃消息,否则将无法提供有效的帮助。 - BaseZen
1
2015-08-29 22:07:26.879 ParseStarterProject-Swift[6339:925415] *** 因未捕获的异常 'NSInvalidArgumentException' 而终止应用程序,原因是:接收器(<ParseStarterProject_Swift.LoginViewController: 0x7fd6f048d7e0>)没有名为“test”的segue。 - Trenton Tyler
我遇到了同样的问题,我调用了performSegueWithIdentifier(),prepareForSegue()被调用了,但是......什么也没有发生......确保performSegueWithIdentifier()在主线程上运行解决了我的问题... - user2962499
2
您很棒!我已经苦恼了好几个星期,这个方法完美解决了我的问题。我从来没有想到主队列可能是问题所在。 - Joseph Astrahan
1
我在使用这段代码时也偶尔会遇到崩溃的情况。在我的情况下,“self”指向UIViewController,它已经被释放(可能是因为用户点击了“返回”按钮),而异步回调被调用的时候。作为解决方案,我将“unowned”更改为“weak”,并调用self?.performSegueWithIdentifier(...)。 - Crulex
@Crulex 我很惊讶之前没有人指出这一点。这是我解决方案中的漏洞。你的诊断完全正确。 - BaseZen

19

确保将以下代码:

self.performSegue(withIdentifier: ..., ...)

放置于viewDidAppear或更晚的方法中。它无法在viewWillAppear或viewDidLoad中正常工作。


另外,不要忘记将视图与视图控制器类链接起来,就像我刚才所做的那样。 - Vilmir

17

我在登录时遇到了同样的问题。可能我们做了同一个教程。在命名segue标识符后,您需要替换:

performSegueWithIdentifier("Klikur", sender: self)

随着:

dispatch_async(dispatch_get_main_queue()){

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

}

在Storyboard Segue中,需要将Seque类型设置为“show(例如Push)”。希望它能起作用。


8
您传递给 performSegueWithIdentifier(_:sender:)的segue标识符必须与您在storyboard中赋予segue的ID 完全匹配。我假设您在登录视图控制器和成功视图控制器之间有一个segue,这正是所期望的;如果没有,请从第一个视图控制器向第二个视图控制器进行ctrl +拖动,然后在storyboard中选择segue的图标并将其ID设置为 Klikur 不要在按钮单击时执行导航,因为这会破坏具有segue的主要目的,即在storyboard中提供应用程序流程的可视指示。

编辑:这里是登录视图控制器的代码:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!

    @IBAction func attemptLogin(sender: AnyObject) {
        if !usernameField!.text!.isEmpty && !passwordField!.text!.isEmpty {
            performSegueWithIdentifier("Klikur", sender: self)
        }
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if "Klikur" == segue.identifier {
            // Nothing really to do here, since it won't be fired unless
            // shouldPerformSegueWithIdentifier() says it's ok. In a real app,
            // this is where you'd pass data to the success view controller.
        }
    }

}

这是我所说的segue属性的截图: 在此输入图片描述


注:segue指的是iOS开发中的视图控制器之间的跳转方式。

你能否发布一个故事板中视图控制器和segue的截图,以及segue的属性面板截图? - NRitH
它们已经被添加了。 - Trenton Tyler
第二张截图中的属性是针对名为“Klikur”的视图控制器的。我需要看到一个ID为“Klikur”的segue。在故事板中将您的视图控制器分开一点,您就会看到segues。应该有一个从登录场景到成功场景的segue,它的ID应该是“Klikur”。 - NRitH

7

swift 3.x

DispatchQueue.main.async(){
    self.performSegue(withIdentifier: "Klikur", sender: self)
}

5
DispatchQueue.main.async() {
    self.performSegue(withIdentifier: "GoToHomeFromSplash", sender: self)`  
}

在你的代码中添加一些注释,以便让人们理解你想要传达的内容。 - Nipun

1

登录示例。当您在单击按钮(操作)后成功登录时,可以使用以下内容:

self.performSegue(withIdentifier: "loginSucess", sender: nil)

但如果您正在启动应用程序并从钥匙串中获取凭据,则需要将其作为线程的一部分使用:

DispatchQueue.main.async(){
    self.performSegue(withIdentifier: "sessionSuccess", sender: nil)
}

1

请检查是否在可见的视图控制器上运行perform segue。

这是一个边缘情况,但当我尝试在不可见的UIPageViewController中运行perform segue时,它会失败。如果我尝试在所有UIPageViewController的视图控制器上执行segue,包括当前可见的视图控制器,也会失败。解决方法是跟踪UIPageViewController中当前可见的视图控制器,并仅在该视图控制器上执行segue。


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