Pass UIView events across layers to VC in iOS

In iOS projects, usually our detail page UI is relatively complex, which often leads to a deep view hierarchy. In this case, if the internal UIView recognizes an event, it is thrown to VC for specific processing. , it needs to be transferred layer by layer, which is very troublesome.

This article is to introduce a way to solve the layer-by-layer transfer direct VC.

1. The question leads

The following is a detailed screenshot of a taxi app. We analyze 打赏司机the hierarchical structure of the page and button: UIButton - "ZLToolView - "ZLHeaderView - "UITableView - "ZLContentView - "VC.view -" VC.

Failed to save, it is recommended to upload the image file directly

Now, if we click 打赏司机, we need VC to execute the logic.

1. Regardless of using a proxy or a block, the link is very long.

This event is firstly ZLToolViewadded to the button click monitor, and then needs to be transferred to the parent view ZLHeaderView, and then called back to UITableViewthe parent view ZLContentView, and finally called back to the VC.

2. Use global notifications

If you use notifications, you can achieve cross-layers, but the disadvantage is that notifications are more suitable for global one-to-many scenarios, and a UI event is still a one-to-one relationship; in addition, too many notifications are not conducive to maintenance.

Two, the solution

One solution I think about the above problem is to implement Viewcross-layer throwing of events from the inner layer through the responder chain VC. The reasons are as follows:

  • The responder chain exists after the UIView is added to the Superview, so there is no need to create an additional chain of responsibility (the responder chain is actually a chain of responsibility design pattern).
  • Passing through the response chain allows any parent in the chain that cares about this event to intercept it.
  • What is achieved is a one-to-one response.

The specific packaging code is as follows:

// 事件需要根据需要去扩展
enum ZLInnerViewEvent {
    case refreshABC // 刷新ABC
    case refreshDetail // 刷新详情接口,如果有参数,可以通过枚举关联值传递
}

protocol ZLInnerEventResponsible: UIResponder {
    func innerEventHandle(type: ZLInnerViewEvent)
}

extension UIView {
    // 一个沿着响应链向上传递事件的方法,bubble=冒泡
    func bubbleEvent(_ eventType: ZLInnerViewEvent) {
        var nextRespnder = self.next
        while nextRespnder != nil {
            if let savior = nextRespnder as? XLInnerEventResponsible {
                savior.innerEventHandle(type: eventType)
                nextRespnder = nil
            }
            nextRespnder = nextRespnder?.next
        }
     }
}

use:

// 内层view传递事件
@objc func reloadBtnAction() {
    self.bubbleEvent(.refreshABC)
}
// VC或响应链中感兴趣的类遵守协议ZLInnerEventResponsible
extension VC: ZLInnerEventResponsible {
    func innerEventHandle(type: ZLInnerViewEvent) {
        switch type {
        case .refreshABC:
            print("refreshABC---")
        case .refreshDetail:
            print("refreshDetail---")
        }
    }
}

Guess you like

Origin juejin.im/post/7247324653840039995