iOS 响应链和事件

iOS Responder Chain and Events

我有点困惑。 UINavagationController的堆栈中有两个UIViewControllers。我尝试在 second UIViewController 中调用 first UIViewController 的方法。请参阅下面的代码

class VC1: UIViewController {

    @objc func method1() {
        //not called after tap button
    }
}

class VC2: UIViewController {

    let button = UIButton()

    override func viewDidLoad() {
        super.viewDidLoad()

        button.addTarget(nil, action: #selector(VC1.method1), for: .touchUpInside)
    }
}

但是当我点击按钮时没有调用 method1。有什么问题吗?

响应链基于父视图和父视图控制器。所以 VC1 不在 VC2 的响应链上。来自 VC2 的响应链指向它的父节点(UINavigationController);它不会经过 UINavigationController 的任何其他子级。

响应链从 button 开始,经过 button 的祖先视图和视图控制器。由于 VC1 和 VC2 是导航控制器堆栈中的独立项,因此 VC1 不是 VC2 的祖先。他们更像是彼此的兄弟姐妹。所以 VC1 不在 button 的响应链中。

您可以将 VC1 设置为按钮操作的目标:

let vc1: VC1 = ...
button.addTarget(vc1, action: #selector(VC1.method1), for: .touchUpInside)

但这意味着 VC2 需要引用现有的 VC1 实例。你如何获得该参考?这取决于您如何创建 VC2 并将其推送到导航堆栈。

如果 VC1 在代码中创建了 VC2,那么 VC1 在创建 VC2 时可以只给新的 VC2 一个引用 self(即 VC1)。

如果你有一个来自 VC1 的故事板 segue 将 VC2 压入堆栈,那么你需要在 VC1 中实现 prepare(for: UIStoryboardSegue, sender:),并且在该方法中你需要将 self (VC1) 传递给segue (VC2) 的目的地。

  • 推送时,是pushedViewController和navigationController之间的关系。
  • 当您模态呈现时,关系是 presentedViewControllerUIWindow 之间的关系。可以理解,呈现视图控制器和呈现视图控制器之间存在关系,但这只是它们相互引用的问题。他们没有通过响应链管道连接...
  • 只有当 viewController 成为另一个 ViewController 的 childViewController 时,它们的响应链才会按照您认为的方式挂钩,这意味着 childViewController 的 nextResponder 成为其“parentViewController”

————

来自nextResponder docs

For example, UIView implements this method and returns the UIViewController object that manages it (if it has one) or its superview (if it doesn’t). UIViewController similarly implements the method and returns its view’s superview. UIWindow returns the application object. The shared UIApplication object normally returns nil, but it returns its app delegate if that object is a subclass of UIResponder and has not already been called to handle the event.

阅读本文我们了解到 presentingViewController 和 pushingViewController 都不是 viewController 的 superview