当segue是模态时,performSegueWithIdentifier非常慢

48

我有一个简单的表格视图,在表格视图上处理选择操作。此操作跟随一个segue。

如果segue是push类型,下一个视图将立即显示。 如果segue是modal类型,下一个视图要么:

  • 需要6秒左右才能显示
  • 如果我再次点击(第二次点击),则立即显示

我尝试寻找一些思路,但似乎没有适用于我的情况。特别地:

  • 我在主UI线程上执行segue
  • 我的视图很简单(所以viewDidLoad中没有问题)。而且当segue为push类型时几乎立即显示目标视图,这表明加载目标视图没有问题
  • 我尝试将sender参数设置为nil,但结果相同

有人对此有什么想法吗?


你在下一个视图中有什么代码?例如,如果你在viewDidLoad中进行同步网络请求,那可能会导致减速。 - Alex Popov
你是在模拟器上测试还是在实际设备上测试?两者之间的结果可能会有很大差异。 - jherran
你是如何呈现你的转场动画的?是用performSegueWithIdentifier吗?如果是的话,你尝试过 dispatch_async(dispatch_get_main_queue(), {performSegueWithIdentifier(mysegueIdentifier, self)}) 吗? - boidkan
@jherran 我正在模拟器和设备上进行测试。在模拟器上,延迟非常明显,在设备上大约为1-2秒。正如我所提到的,如果我只将segue从“modal”更改为“push”,那么它就是瞬间的。因此问题出在“modal” segue上... - tng
8个回答

94

相信我,试一下这个方法。我遇到过几次这个问题。

在Swift 2中:

dispatch_async(dispatch_get_main_queue(),{
    self.performSegue(withIdentifier:mysegueIdentifier,sender: self)
})

或者适用于 Swift 3:

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

如此讨论在这里这里


1
我尝试过了 - 对我没用,正如我所提到的,我已经验证过我已经在主线程上了,所以调度不起作用... - tng
2
因为它发生在模态转场中,大约需要6秒钟的时间,但对第二次点击立即响应。每次我遇到这个问题时,都涉及到相同的事情,并且使用上面答案中的代码解决了问题。非常惊讶它对你不起作用。尝试通过打开/关闭动画并且不显示当前上下文来调整一下? - boidkan
3
所以我最终重写了一堆代码,使用dispatch_async似乎解决了我的问题。我无法解释为什么,因为我确定我在UI线程上,但就这样吧。 - tng
你能告诉我你做了什么吗?我遇到了同样的问题,系统对于isMainThread打印出true,但是dispatch_sync也会导致我的应用程序冻结。 - Razvan Soneriu
1
我在应用程序的一个屏幕上遇到了这个问题。我从单元格中删除了到模态屏幕的segue,并在控制器级别添加了一个segue,在didSelectRowAtIndexPath上触发它。我使用assert(NSThread.isMainThread())验证代码是否在主线程上运行。但问题仍然存在。我无法解释为什么,但将“performSegue”调用推迟到下一个运行循环可以解决此问题。 - Eneko Alonso
显示剩余8条评论

7

在我看来,这个问题似乎只发生在单元格 selectionType 不是 .none 的情况下。

你可以将其更改为任何其他选项(在故事板的 Attribute inspector 中设置 Selection 字段),它会正常工作(对我而言是有效的)。缺点是它会破坏单元格的界面。

你可以在 UITableViewDelegatedidSelect 委托函数中调用 DispatchQueue.main.async{} 代码块来执行跳转,就像之前有人提到的那样。

我使用了第一个解决方案,并将其添加到了单元格本身中 -

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(false, animated: false)
}

这将使单元格在轻触时“高亮”,但它会立即返回到其通常的UI,对我来说就很好...


1
你说得对, 我也遇到了同样的问题,我的单元格选择类型是.none,然后就出现了延迟,所以我尝试调用“DispatchQueue.main.async”执行segue,它起作用了,然后我尝试了你的解决方案,现在segue立即出现了。当选择类型为.none时,肯定有什么问题。 - xlsmearlx

4
执行转场时可能会遇到各种问题。例如,如果您在解缠转场的操作处理程序中调用performSegue,即使您在主线程上,也会遇到各种问题。在我的当前项目中,我正在从一个表视图的didSelectRowAt方法中调用performSegue。这是最基本的转场之一,当然我在主线程上,但我看到了OP所描述的确切症状。

我不知道为什么有些情况下会出现这种情况,而有些情况下则没有,但我发现使用async推迟performSegue调用可以修复任何潜在问题。这曾经看起来像是一个hack,让我感到紧张,但此时我有几个成熟的项目使用这种方法,现在它似乎是手动转场的“正确”方式。

以下是Swift 3版本的代码(有关Swift 2和Obj-C版本,请参阅其他帖子):

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

2

这个被接受的解决方案对我有用。下面是针对Swift 2.0更新的代码:

dispatch_async(dispatch_get_main_queue(),{
     self.performSegueWithIdentifier(mysegueIdentifier, sender:self)
})

1
希望能帮助你,让你可以在Swift中编程地创建模态转换,就像这样:
       let myStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let modalViewController = myStoryboard.instantiateViewControllerWithIdentifier("myModalViewController") as! myModalViewController
        modalViewController.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
        let navController = UINavigationController(rootViewController: accountManager)
        dispatch_async(dispatch_get_main_queue(),{
            self.presentViewController(navController, animated: true, completion: nil)
        })

1

对于通过子类化组织代码的开发人员,我想分享一个非常简单的解决方案(Swift 4):

import UIKit

class ABCViewController: UIViewController {

  // ... Other useful methods like overriding deinit and didReceiveMemoryWarning

  // Performs a segue on the main thread to ensure it will 
  // transition once a UI-slot is available
  func performSegueOnMainThread(with identifier: String, sender: Any?) {
    DispatchQueue.main.async {
      self.performSegue(with: identifier, sender: sender)
    }
  }
}

然后,只需从您的实现中调用它:
myViewController.performSegueOnMainThread(with: "ShowDetailsSegue", sender: self)

0
对我来说,问题出在我的下一个视图中有太多“透明”颜色的视图,因此当segue进行动画时,它似乎因此而延迟。我查看了视图控制器的UI层次结构,寻找透明颜色并将其替换为纯黑色或白色,并确保alpha为1(如果不需要其他值)。现在我的延迟问题已经解决,我的模态呈现也很流畅。

-1

我尝试了多种方法来解决这个问题,包括将其移至主线程之上。以下方法对我有效:

在Storyboard中,选择相关的表格视图(在文档大纲中选择以确保你所选的是正确的)。然后,在属性检查器中,你可以看到表格视图和它下面的滚动视图(所有表格视图都基于滚动视图)的属性。那里有一个愚蠢的小框,叫做“延迟内容触摸”。取消勾选它。还有一个“允许可取消的触摸”,我想你也应该确保它没有被勾选,因为我认为双击会弄乱它。


如果这样做,你会使得在表格视图中滚动变得更加困难,因为单元格内的滑动可能无法被识别。 - phatmann

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