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.
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 ZLToolView
added to the button click monitor, and then needs to be transferred to the parent view ZLHeaderView
, and then called back to UITableView
the 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 View
cross-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---")
}
}
}