统一的UIViewController“成为前台”检测?

33

在我的看法中,下列情况属于同一范畴:

  • 我的视图控制器全屏呈现了另一个视图控制器,现在已被解除呈现

  • 我的视图控制器呈现了另一个不全屏的视图控制器,现在已被解除呈现

  • 我的视图控制器呈现了一个弹出窗口,现在已被解除呈现

  • 我的视图控制器推入了另一个视图控制器,现在已被弹出

在每种情况下,我的视图控制器停止成为“前端”视图控制器,然后重新变为“前端”。我觉得iOS没有单独的“成为前端”事件发送到我的视图控制器,覆盖所有这些情况,这很奇怪。

我认为我可以分别处理上述每种情况,并且我认为这些都是我需要解决的所有情况,但生成的代码很混乱和分散:

  • viewDidAppear 检测推动的视图控制器的弹出和全屏呈现视图控制器的解除呈现

  • 弹出窗口代理消息检测弹出窗口的解除呈现

  • 不确定是什么检测了非全屏呈现视图控制器的解除呈现

人们如何处理这个问题使其具有连续性和优雅性?


我在使用 UIViewControllerbecomeFirstResponderresignFirstResponder 时,取得了非常一致的结果。详情请见这里。但是对于子视图控制器,这种方法并不起作用。 - bteapot
@bteapot 我从来没有想过那个,我会研究一下,谢谢! - matt
好问题。我已经苦苦思索了一段时间(一直在推迟),希望能找到一种方法,在控制器之间注入代理响应对象(交换?),以便在不将代码溢出到“否则无上下文”的控制器的情况下检测这些转换。 - Chris Conover
我在玩弄becomeFirstResponder时遇到了一些问题——当一个呈现的视图控制器被解除显示时,它似乎不会被调用到新的最前面。我认为这并不是很令人惊讶,因为它可能会触发可能会干扰的UI,但这意味着它不能用于此目的。 - adamz
2个回答

15

这些案例的共同点不是原始视图控制器的出现,而是呈现/推送的视图控制器的消失。因此,一个简单明了的解决方案似乎是采用协议和代理架构。声明一对协议,如下所示:

protocol Home : class {
    func comingHome()
}
protocol Away : class {
    var home : Home? {get set}
}
extension Away where Self : UIViewController {
    func notifyComingHome() {
        if self.isBeingDismissed || self.isMovingFromParent {
            self.home?.comingHome()
        }
    }
}
  • 主页视图控制器必须采用 Home 协议,并在呈现或推出视图控制器时将每个视图控制器的 home 设置为 self

  • 被呈现或推出的视图控制器必须采用 Away 协议,并按以下方式实现 viewWillDisappear 方法:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.notifyComingHome()
}

这适用于问题中列出的四种情况。然而,可惜的是,Cocoa Touch不会自动为您完成此操作。


编辑现在,在iOS 13强制非全屏呈现的视图控制器之后,这种方法在我的应用程序中变得更加重要。另外,我已经对UIAlertController进行了子类化,使其符合Away。


暂时接受我的答案,但如果有更好的方法,我想听听。 - matt
当您呈现包含另一个视图控制器的视图控制器时,此方法无效。在解除时,这两个视图控制器都会返回“isBeingDismissed”和“isMovingFromParent”为false。 - Andrey Gordeev
2
另外,在iOS 13上,即使您只是开始向下拖动模态视图控制器,viewWillDisappear也会被调用。但是,开始向下拖动VC并不意味着它将被解除显示。 - Andrey Gordeev
4
好的,给你一个更好的答案。我需要一个答案。 - matt
1
@AndreyGordeev @matt 如果有人需要,我在这里留下了它:我将 self.home?.comingHome() 实现为在 viewDidDisappear 中而非 viewWillDisappear,这样只有在视图实际被关闭(即在拖动后)才会回调。 - vincent

2

一种解决方案可以采用类似MVVM-C风格架构中的协调器方法。您永远不会直接在VC中更改视图层次结构,而是始终调用协调器来为您执行操作。 coordinator.showDetails(...)

此外,在您的VC中定义一个viewDidBecomeForemost方法,协调器可以在返回到VC时调用它。


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