事情呼应链是 iOS 开发中的一个核心概念,它描述了体系将用户交互事情传递给最适合处理该事情的方针的过程。了解事情呼应链的机制对于开发高质量的使用程序至关重要。本文将深入探讨事情呼应链的作业原理,并供给 Swift 中的代码示例来帮助读者更好地了解这一概念。

事情呼应链的作业原理

iOS 中,事情呼应链的作业原理能够简单概括为:从最上层的 UIWindow 开端,顺次向下传递事情,直到找到最适合处理事情的呼应者方针为止。在这个过程中,每个呼应者方针都有机会处理事情。

当用户履行一个操作时,如接触屏幕或运动设备,体系会创立一个 UIEvent 方针,并将其发送到当时的榜首呼应者方针。假如榜首呼应者方针无法处理该事情,则体系会将该事情传递给呼应者链中的下一个方针,直到找到能够处理该事情的方针。假如最终没有方针能够处理该事情,则事情被体系丢掉。

以下是事情呼应链的示意图:

         UIWindow
             |
       UIViewController
             |
           UIView
             |
     subviews of UIView

在这个示意图中,UIWindow 是呼应者链的起点,它是一切视图的根视图。UIViewControllerUIView 都是呼应者方针,它们都能够处理事情。UIViewController 能够接纳和处理来自其根视图的事情,而 UIView 能够接纳和处理来自其本身的事情,以及来自其子视图的事情。

呼应者方针的特点

呼应者方针是一种特别类型的方针,它们完成了 UIResponder 类。呼应者方针能够处理事情,能够成为榜首呼应者方针,而且能够将事情传递给下一个呼应者方针。以下是 UIResponder 类中的一些常用办法:

  • canBecomeFirstResponder:回来一个布尔值,指示方针是否能够成为榜首呼应者方针。
  • becomeFirstResponder:将方针设置为榜首呼应者方针。
  • resignFirstResponder:抛弃榜首呼应者方针的方位。
  • next:回来呼应者链中的下一个呼应者方针。

呼应者方针还能够完成许多办法来处理事情,例如:

  • touchesBegan(_:with:):当用户在视图上开端接触时调用。
  • touchesMoved(_:with:):当用户在视图上移动接触时调用。
  • touchesEnded(_:with:):当用户在视图上结束接触时调用。
  • touchesCancelled(_:with:):当体系撤销接触事情时调用。

自界说事情处理

Swift 中,能够经过重写 UIResponder 子类的办法来自界说事情处理。以下是一个示例代码,展现如何重写 UIView 子类的 touchesBegan 办法来处理接触事情:

class CustomView: UIView {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // 处理接触事情
        // ...
    }
}

在这个示例中,当用户在 CustomView 上开端接触时,体系会调用 CustomViewtouchesBegan 办法。在此办法中,开发者能够编写自己的接触事情处理代码。

事情传递和事情呼应

事情传递和事情呼应是事情呼应链的两个重要环节。在事情传递阶段,体系会将事情从上往下传递,直到找到最适合处理事情的方针。在事情呼应阶段,体系会将事情从下往上呼应,直到事情被处理或者传递到呼应者链的顶部。

在事情传递阶段,UIViewUIViewController 都有一个 hitTest( *:with:) 办法,该办法回来一个 UIView 方针。当体系接纳到事情时,它会调用 hitTest(* :with:) 办法来确定最适合处理该事情的视图方针。hitTest( *:with:) 办法首先查看自己是否能够处理该事情,假如不能,它会将事情传递给其子视图,并递归调用子视图的 hitTest(* :with:) 办法,直到找到能够处理该事情的视图方针。

在事情呼应阶段,体系会将事情传递到榜首呼应者方针,并沿着呼应者链向上传递,直到事情被处理或者传递到呼应者链的顶部。在这个过程中,每个呼应者方针都有机会处理事情。假如某个呼应者方针能够处理事情,则它将调用相应的办法来处理事情,例如 touchesBegan(_:with:) 办法。假如该方针不能处理事情,则它将调用 next 办法,将事情传递给呼应者链中的下一个方针。

事情阻拦

hitTest(_:with:) 办法中,咱们能够查看接触点是否在指定区域内,假如在,则回来当时视图作为阻拦方针,不然回来 nil,让体系将事情传递给下一个呼应者。示例代码如下:

class CustomView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if self.bounds.contains(point) {
            // 接触点在视图内,阻拦事情
            return self
        } else {
            // 接触点不在视图内,将事情传递给下一个呼应者
            return super.hitTest(point, with: event)
        }
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        print("CustomView touchesBegan")
    }
}

在上述代码中,咱们重写了 hitTest( *:with:) 办法,并在该办法中查看接触点是否在视图内。假如在,则回来当时视图作为阻拦方针,不然回来 nil,让体系将事情传递给下一个呼应者。在 touchesBegan(* :with:) 办法中,咱们打印了一条日志,以便在接触事情产生时能够看到该办法是否被调用。

事情传递到父视图

要将事情传递到父视图,能够调用 next?.touchesBegan(touches, with: event) 办法,让父视图处理接触事情。示例代码如下:

class CustomView: UIView {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // 处理接触事情
        // 假如无法处理该事情,传递给父视图进行处理
        next?.touchesBegan(touches, with: event)
    }
}
class ParentView: UIView {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // 处理接触事情
    }
}

在上面的示例中,CustomViewParentView 都是 UIView 的子类。当用户在 CustomView 上接触时,CustomViewtouchesBegan 办法会被调用。在这个办法中,假如 CustomView 无法处理该事情,它会将该事情传递给其父视图(ParentView)进行处理。

经过 next?.touchesBegan(touches, with: event) 办法将事情传递给父视图,假如父视图能够处理该事情,它的 touchesBegan 办法将被调用。在这个办法中,能够处理接触事情。假如父视图仍然无法处理该事情,该事情将被传递给更高等级的呼应方针进行处理。

需要留意的是,当事情被传递到下一个呼应方针时,会调用该方针的 touchesBegan 办法。因此,在这个办法中,能够对事情进行处理,也能够将其传递给更高等级的呼应方针进行处理。

自界说事情呼应链

iOS 中,每个视图都是一个 UIResponder 方针。UIResponder 是一个抽象类,它界说了呼应者方针能够处理的事情类型,包含接触事情、加快计事情、长途控制事情等。每个 UIResponder 方针都有一个 next 呼应者,即下一个呼应者方针。当一个事情产生时,体系会将该事情早年往后顺次传递给呼应者链中的方针,直到某个方针处理了该事情为止。假如没有任何方针处理该事情,则该事情将被丢掉。

咱们能够经过自界说 UIResponder 子类来完成更灵敏的事情处理逻辑。下面是一个简单的自界说呼应者链的示例代码:

class CustomResponder: UIResponder {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        print("CustomResponder touchesBegan")
        next?.touchesBegan(touches, with: event)
    }
}
class ViewController: UIViewController {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        print("ViewController touchesBegan")
    }
}

在上面的代码中,咱们界说了一个名为 CustomResponder 的自界说呼应者子类。在该类中,咱们重写了 touchesBegan(_:with:) 办法,并在该办法中打印了一条日志。在该办法中,咱们还调用了 next?.touchesBegan(touches, with: event) 办法,将接触事情传递给下一个呼应者方针。

在 ViewController 中,咱们也重写了 touchesBegan( *:with:) 办法,并在该办法中打印了一条日志。当接触事情产生时,首先会调用 CustomRespondertouchesBegan(* :with:) 办法,并打印出 “CustomResponder touchesBegan“。然后,由于咱们调用了 next?.touchesBegan(touches, with: event) 办法,体系会将接触事情传递给 CustomResponder 的下一个呼应者方针,即 ViewController。此刻,体系会调用 ViewControllertouchesBegan(_:with:) 办法,并打印出 “ViewController touchesBegan“。

经过自界说呼应者子类,咱们能够更加灵敏地处理事情,完成更杂乱的事情处理逻辑。例如,咱们能够在呼应者链中添加多个呼应者方针,根据事情类型、接触点方位等条件来决定哪个呼应者方针处理该事情。

总结

事情呼应链是 iOS 开发中的一个核心概念。了解事情呼应链的作业原理和完成方式,能够帮助开发者更好地了解 iOS 使用的交互模型,编写更高效、牢靠的交互代码。

以下是一些事情呼应链的实践建议:

  • 在处理接触事情时,始终调用父类的 touchesBegan( *:with:)touchesMoved(* :with:)touchesEnded( *:with:)touchesCancelled(* :with:) 办法。这样能够保证事情会正确地传递到呼应者链的下一个方针。
  • 假如期望在事情传递过程中阻拦事情,能够重写 hitTest(_:with:) 办法,并在该办法中查看事情是否应该被阻拦。
  • 假如期望将事情传递到父视图,能够调用 next?.touchesBegan(touches, with: event) 办法。
  • 尽可能防止运用 touches 特点,由于该特点在多点触控环境下会出现问题。引荐运用 allTouches 特点来获取一切接触点。
  • 尽可能防止运用 gesture recognizer 来处理接触事情,由于这会增加体系的负担,并可能导致意外的行为。引荐运用接触事情处理办法来处理接触事情。

期望本文能够帮助开发者更好地了解 iOS 使用的交互模型,并编写更高效、牢靠的交互代码。