警告:尝试在已经显示(null)的*上呈现*

79

这是我为iOS编写的第一个应用程序。

因此,我拥有一个带有UITableViewUIVIewController,在其中集成了UISearchBarUISearchController,以便过滤要显示的TableCells。

override func viewDidLoad() {
    menuBar.delegate = self
    table.dataSource = self
    table.delegate = self
    let nib = UINib(nibName: "ItemCellTableViewCell", bundle: nil)
    table.registerNib(nib, forCellReuseIdentifier: "Cell")

    let searchButton = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: "search:")
    menuBar.topItem?.leftBarButtonItem = searchButton
    self.resultSearchController = ({
        let controller = UISearchController(searchResultsController: nil)
        controller.searchResultsUpdater = self
        controller.dimsBackgroundDuringPresentation = false
        return controller
    })()
    self.table.reloadData()
}

我还使用了一个模态转换来打开元素的ViewController,在其中我将显示元素的详细信息。

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.index = indexPath.row
    self.performSegueWithIdentifier("ItemDetailFromHome", sender: self)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "ItemDetailFromHome") {
        let settingsVC = segue.destinationViewController as! ItemDetailViewController
        settingsVC.parent = self
        if self.isSearching == true  && self.searchText != nil && self.searchText != ""  {
            settingsVC.item = self.filteredItems[self.index!]
        } else {
            settingsVC.item = self.items[self.index!]
        }

    }
}

当我尝试通过UISearchController筛选元素并显示ItemDetailViewController时,一切都很好,但问题出现了。

我收到以下消息:

Warning: Attempt to present <ItemDetailViewController: *>  on <HomeViewController: *> which is already presenting (null)

每次我都要进入 ItemDetailViewController.viewDidLoad() 函数,但之后当搜索被激活时,我遇到了之前的错误。

有什么想法吗?我尝试使用以下异步调度程序,但没有成功。

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.index = indexPath.row
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.performSegueWithIdentifier("ItemDetailFromHome", sender: self)
    })
}

如果您正在从表视图控制器中设置ItemDetailFromHome,那么在did select row at index path中不需要调用self.performSegueWithIdentifier("ItemDetailFromHome", sender: self)。 - Johnykutty
事实上,ItemDetailViewController将是由多个segue/viewController(共用)使用的视图。 - Splendf
很好,我该怎么在cellForRowAtIndexPath中实现呢?我只看到了使用performSegue的例子。 - Splendf
1
如果您已经在Storyboard中从TableView单元格添加了segue,则无需编写self.performSegue...代码。如果您从视图控制器添加了segue,则需要这样做。 - Johnykutty
@Johnykutty 谢谢!当我在重构一个项目时,我发现我遇到了这个问题。感谢您发布如此简单的解决方案。 - Adrian
18个回答

84

我已经找到了解决方案。

我在 HomeViewController.viewDidLoad 中添加了以下代码,它可以正常工作!

definesPresentationContext = true

13
@Shahar 这段代码之所以有效,是因为 definesPresentationContext 的作用是告诉系统,“当你呈现另一个模态视图控制器时,使用我这个视图控制器”。否则,系统会沿着视图层次结构向上查找已经设置为definesPresentationContext 为真的视图控制器,并使用它来呈现,在这种情况下,可能已经有其他视图控制器在呈现了。 - cohen72
1
由于某些原因,在将视图控制器嵌入UINavigationController之后,这才起作用。 - Crashalot
1
我的应用在iOS 10和11上运行得非常完美,但是当我在iPhone 4上测试时,开始出现了这个错误。这个解决方案非常有效。顺便说一下,您还可以在Storyboard编辑器中选择视图控制器并启用“定义上下文”复选框来设置definesPresentationContext。 - Felipe Ferri
终于找到解决一个困扰我很长时间的问题的方法了。谢谢您,先生。 - iDoc
1
我认为这是一种hack,因为它只是治疗了症状而不是根本原因。 - Mojo66
这是一种hack方法,因为成功取决于视图控制器的层次结构。 - Blazej SLEBODA

37
在我的情况下,我发现我的代码呈现新的视图控制器(一个UIAlertController)被调用了两次。 在动手处理definesPresentationContext之前,请检查这一点。

11
听起来有些傻,但我敢打赌在绝大多数情况下这将是正确的答案。果然我找到了第二通电话……(注意:此处缺少上下文信息,无法确定具体翻译意图,因此仅提供字面翻译) - the_dude_abides
1
是的,在某些情况下,我们需要在呈现之前检查 presentedViewController - onmyway133
在我的情况下,它没有被调用两次,但我在shouldPerformSegue()内部调用了present(),并忘记返回false以防止故事板segue呈现某些内容。 - randomcontrol
我们又发现了第二个对 present 的调用。 - Aaron

13

在我的情况下,我尝试在关闭前一个视图控制器之前过早地显示新的视图控制器。通过稍微延迟调用来解决了这个问题:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
     self.callMethod()
}

33
我不建议使用随机的0.3秒延迟。请尝试找到正确的方法,然后在视图呈现之后再进行操作(viewDidAppear),而不是希望层次结构在0.3秒后准备好。 - Allison
这个答案应该被踩,因为它是一个丑陋的黑客。 - Mojo66
3
在我的情况下,它让我看到了第二个视图控制器,由于错误添加了呈现。它可能被用作调试工具而非解决方案。 - Rostyslav Druzhchenko

8

对我起作用的是将警告框的显示添加到主线程中。

DispatchQueue.main.async {
   self.present(alert, animated: true)
}

当前视图控制器的显示还未完成。通过将警告添加到主线程,可以等待视图控制器的显示完成后再尝试展示。


这就是我的问题!我在后台线程上,忘记了。 - teradyl

7
对我来说,问题在于我展示了两个模态框,我必须关闭两个模态框并在父窗口执行一些代码...然后我遇到了这个错误...我通过在关闭最后一个模态框时设置此代码解决了这个问题。
self.dismiss(animated: true, completion: {
                self.delegate?.callingDelegate()
            })

换句话说,不要只简单地调用两次dismiss...在第一个dismiss的完成块中调用委托来执行第二个dismiss。

5

当我尝试在 SideMenu(jonkykong) 中调用一个视图控制器时,遇到了相同的问题。

首先我在 SideMenu 中尝试,然后从委托中调用到 MainVC,两者都出现了相同的问题。

解决方案:先关闭 SideMenu,然后再呈现新的视图控制器,就可以完美解决!


4
在我们的项目中出现了这种情况。我将我们的登录/注销ViewController作为弹出窗口进行展示。但是每次我尝试再次注销并显示弹出窗口时,我的控制台都会显示已注销:

Warning: Attempt to present UIViewController on <MY_HOME_VIEW_CONTROLLER> which is already presenting (null)

我猜测即使不可见,我的ViewController仍然持有弹出窗口。

无论如何,如果您正在尝试显示新的ViewController,以下代码可以解决此问题:

func showLoginForm() {

    // Dismiss the Old
    if let presented = self.presentedViewController {
        presented.removeFromParentViewController()
    }

    // Present the New
    let storyboard = UIStoryboard(name: "MPTLogin", bundle: Bundle(for: MPTLogin.self))
    let loginVC = storyboard.instantiateViewController(withIdentifier: "LogInViewController") as? MPTLogInViewController
    let loginNav = MPTLoginNav(rootViewController: loginVC!)
    loginNav.modalPresentationStyle = .pageSheet;
    self.present(loginNav, animated: true, completion: nil)
}

1
我的问题非常相似。事实证明,我在紧随一个视图控制器的解除调用之后立即跟着另一个呈现调用。误导人的是,这实际上在十次中有九次都能正常工作! - Wade

3

Mehrdad的回答基础上:我首先需要检查搜索控制器是否处于活动状态(即用户当前是否正在搜索):

if self.searchController.isActive {
    self.searchController.present(alert, animated: true, completion: nil)
} else {
    self.present(alert, animated: true, completion: nil)
}

其中alert是用于模态呈现的视图控制器。


这正是我的问题。我总是直接从应用程序视图控制器中呈现新的视图控制器(在我的情况下是PHPickerViewController),即使搜索控制器处于活动状态。修复的编码方式是调用self.navigationController!.visibleViewController!.present。 - xirix

2
我遇到了同样的问题,我的做法是从Interface Builder中选择我的Segue,它的类型为"模态呈现",而它的展示方式是"在当前上下文之上"。我把展示方式改成了"默认(Default)",然后问题得到了解决。

2
在我的情况下,我尝试在应用程序的某个时间点显示一个UIAlertController,在同一UINavigationController中使用了UISearchController。我没有正确使用UISearchController,并忘记在解雇前设置searchController.isActive = false。稍后在应用程序中,我尝试呈现警报,但搜索控制器虽然当时不可见,仍然控制着呈现上下文。

1
太感激了,终于看到了你的答案;我一直在想办法解决这个问题,结果发现我的也是 UISearchController - James Toomey
@JamesToomey,很高兴我能帮上忙!在意识到发生了什么之前,我花费了大量时间来解决自己的问题... - Clay Ellis
如果你在处理UISearchController时遇到了困难,这就是答案! - Carioni

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