iOS Responder Chain或他们在面试中的要求

图片


第一个例子和第二个例子有什么区别?

目标负责人是什么?

在哪种情况下,单击按钮时会调用该方法?

TL; DR


当单击按钮时,在两种情况下都会调用我们的方法。


仅在第一个示例中, UIKit会尝试在分配的目标(我们有ViewController )中调用该方法。 如果此方法不存在,它将崩溃。


在第二个示例中,使用iOS Responder Chain, UIKit将查找具有此方法的最近的UIResponder -a。 如果找不到我们的方法,就不会崩溃。


UIViewController, UIView, UIApplication继承自UIResponder


iOS Responder Chain及其内幕


UIKit iOS Responder Chain过程由UIKit处理, UIKitUIResponder的链表动态配合使用。 此UIKit列表UIKit从第一个响应者(注册事件的第一个UIResponder创建的,我们有UIButton(UIView)及其subviews


图片


UIKit会遍历UIResponder列表,并使用canPerformAction检查我们的功能。


 open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool 

如果所选的UIResponder无法使用特定方法,
UIKit使用返回下一个UIRespondertarget方法将操作递归发送到列表中的下一个UIResponder


 open func target(forAction action: Selector, withSender sender: Any?) -> Any? 

重复此过程,直到其中一个UIResponder可以使用我们的方法或列表结束并且系统忽略此事件为止。


在第二个单击示例中,它是由UIViewController处理的,但是由于UIKit是第一个响应者,因此它首先向UIView发送了一个请求。 它没有必需的方法,因此UIKit将操作重定向到链接列表中的下一个UIResponder ,后者是具有所需方法的UIViewController


在大多数情况下, iOS Responder Chain是一个简单的subviews列表,但是可以更改其顺序。 您可以使UIResponder (becomeFirstResponder)成为
第一个UIResponder并使用resignFirstResponder将其返回到旧位置。 通常将它与UITextField一起使用,以显示仅当UITextFieldfirst responder时才被调用的键盘。


iOS响应链和UIEvent


响应程序链还涉及触摸屏幕,动作,点击。 当系统检测到某种事件(触摸,动作,远程控制,按下)时,将在UIEvent创建一个UIEvent ,并使用UIApplication.shared.sendEvent()方法将其发送到UIWindow 。 收到事件后, UIWindow使用hitTest:withEvent确定此事件所属的UIResponder并将其分配给第first responder 。 接下来是上面描述的UIResponder的链接列表的工作。


要使用系统UIEventUIResponder (UIViewController, UIView, UIApplication)子类UIResponder (UIViewController, UIView, UIApplication)可以覆盖以下方法:


 open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) open func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func pressesChanged(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) open func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) open func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) open func motionCancelled(_ motion: UIEvent.EventSubtype, with event: UIEvent?) open func remoteControlReceived(with event: UIEvent?) 

尽管存在可以手动继承和调用sendEvent功能,但UIResponder并不是为此目的而设计的。 这first responeder自定义事件的操作造成很多问题,这可能导致由随机的first responeder响应者(可以响应您的事件)引起的难以理解的动作。


为什么有用,在哪里使用


尽管iOS Responder ChainUIKit完全控制,但仍可用于解决委派/通信问题。 UIResponder操作类似于一次性NotificationCenter.default.post


让我们举个例子,我们有一个UIViewController ,它位于UINavigationController堆栈的深处,我们需要告诉它在另一个屏幕上单击按钮时发生了什么。 您可以使用delagate模式或NotificationCenter.default.post ,但是一个相当简单的选项是使用iOS Responder Chain


 button.addTarget(nil, action: #selector(RootVC.doSomething), for: .touchUpInside) 

按下时,将调用UIViewController的方法。 #selector可以采用以下参数:


 @objc func doSomething() @objc func doSomething(sender: Any?) @objc func doSomething(sender: Any?, event: UIEvent?) 

sender是发送事件的对象-UIButton,UITextField等。


其他学习资源[eng]:


对UIEvent,UIResponder和几个高级示例的很好描述(模式协调器)
阅读有关ios响应程序链的更多信息
实践中的响应者链示例
iOS响应者链上的离线
UIResponder的离坞

Source: https://habr.com/ru/post/zh-CN464463/


All Articles