由于项目中团队协作,以及UI层级嵌套较多,UI交互,事件传递,使用代理,因为不能够跨层,所以不太合适,而用通知的话,由于是全局的。如果页面不展示的时候,也有可能会触发事件,而且使用完也需要移除。不太符合我这边的预期。经过最近一段时间的思考,最近有了点思路,和大家分享一下。
基于事件响应链事件从顶层向下层传递
主要是发送事件,同时通过nextResponder来判断是否处理事件,如果能够处理则处理,如果忽略,那么接着向下层传递,直到window层。当然了如果响应了该事件,也可以让该事件接着向下层传递。
基于UI的视图层级,从底层向顶层传递事件
从某个视图开始,发送事件,事件的传递范围仅限于该视图的所有子视图。如果相应的子视图遵守某个协议,事件传递来了,就处理相应的逻辑,当然了如果某个事件只需要某一个子视图处理一次,那么处理完以后,可以把结果返回终止掉事件的后续传递。
具体代码如下:
typedef NS_ENUM(NSInteger, JKUIEventResult)
{
JKUIEventResultIgnore = 0, /// 忽略事件,事件会继续传递,直到结束
JKUIEventResultHandle, /// 响应事件,阻止事件继续传递
JKUIEventResultHandleDelivery /// 响应事件,并让事件继续传递
}; /// 事件处理结果
@protocol JKUIEventProtocol <NSObject>
@optional
/// 接收到链式事件
/// @param eventName 事件名称
/// @param data 数据
- (JKUIEventResult)jk_receiveChainEvent:(nonnull NSString *)eventName
data:(nullable id)data;
/// 接收广播事件
/// @param eventName 事件名称
/// @param data 数据
- (JKUIEventResult)jk_receiveBroadcastEvent:(nonnull NSString *)eventName
data:(nullable id)data;
@end
@interface JKUIEventHandler : NSObject
/// 发送链式事件,事件根据响应链朝着对应的VC层级传递
/// @param eventName 事件名称
/// @param data 数据
/// @param responder 响应者
+ (void)sendChainEvent:(nonnull NSString *)eventName
data:(nullable id)data
responder:(nonnull __kindof UIResponder *)responder;
/// 广播事件,事件根据视图层级,从responder层朝着顶层传递
/// @param eventName 事件名称
/// @param data 数据
/// @param responder 响应者
+ (void)broadcastEvent:(nonnull NSString *)eventName
data:(nullable id)data
responder:(nonnull __kindof UIResponder *)responder;
@end
#import "JKUIEventHandler.h"
#import "JKUIEventProtocol.h"
@implementation JKUIEventHandler
+ (void)sendChainEvent:(nonnull NSString *)eventName
data:(nullable id)data
responder:(nonnull __kindof UIResponder *)responder
{
#if DEBUG
NSAssert(eventName, @"eventName can't be nil");
NSAssert(responder, @"responder can't be nil");
NSAssert([responder isKindOfClass:[UIResponder class]], @"make sure [responder isKindOfClass:[UIResponder class]] be YES");
#endif
if (!eventName) {
return;
}
if (!responder) {
return;
}
if (![responder isKindOfClass:[UIResponder class]]) {
return;
}
UIResponder <JKUIEventProtocol>*currentResponder = (UIResponder <JKUIEventProtocol>*)responder.nextResponder;
while (currentResponder
&& ![currentResponder isKindOfClass:[UIWindow class]]
&& ![currentResponder isKindOfClass:[UIApplication class]]) {
if ([currentResponder conformsToProtocol:@protocol(JKUIEventProtocol)]) {
if ([currentResponder respondsToSelector:@selector(jk_receiveChainEvent:data:)]) {
JKUIEventResult result = [currentResponder jk_receiveChainEvent:eventName data:data];
if (result == JKUIEventResultHandleDelivery) {// 事件继续传递
currentResponder = (UIResponder <JKUIEventProtocol>*)currentResponder.nextResponder;
} else {
break;
}
} else {
currentResponder = (UIResponder <JKUIEventProtocol>*)currentResponder.nextResponder;
}
} else {
currentResponder = (UIResponder <JKUIEventProtocol>*)currentResponder.nextResponder;
}
}
}
+ (void)broadcastEvent:(nonnull NSString *)eventName
data:(nullable id)data
responder:(nonnull __kindof UIResponder *)responder
{
#if DEBUG
NSAssert(eventName, @"eventName can't be nil");
NSAssert(responder, @"responder can't be nil");
NSAssert([responder isKindOfClass:[UIResponder class]], @"make sure [responder isKindOfClass:[UIResponder class]] be YES");
#endif
if (!eventName) {
return;
}
if (!responder) {
return;
}
if (![responder isKindOfClass:[UIResponder class]]) {
return;
}
if ([responder isKindOfClass:[UIViewController class]]) {
__kindof UIViewController <JKUIEventProtocol>*responderVC = (__kindof UIViewController <JKUIEventProtocol>*)responder;
if ([responderVC conformsToProtocol:@protocol(JKUIEventProtocol)]
&& [responderVC respondsToSelector:@selector(jk_receiveBroadcastEvent:data:)]) {
JKUIEventResult result = [responderVC jk_receiveBroadcastEvent:eventName data:data];
if (result == JKUIEventResultHandle) { // 事件停止继续广播
return;
}
}
if (responderVC.childViewControllers.count > 0) {
for (__kindof UIViewController *childVC in responderVC.childViewControllers) {
[self broadcastEvent:eventName data:data responder:childVC];
}
}
__kindof UIView <JKUIEventProtocol>*view = (__kindof UIView <JKUIEventProtocol>*)responderVC.view;
if ([view conformsToProtocol:@protocol(JKUIEventProtocol)]
&& [view respondsToSelector:@selector(jk_receiveBroadcastEvent:data:)]) {
JKUIEventResult result = [view jk_receiveBroadcastEvent:eventName data:data];
if (result == JKUIEventResultHandle) { // 事件停止继续广播
return;
}
}
if (responderVC.view.subviews.count > 0) {
for (__kindof UIView *subview in responderVC.view.subviews) {
[self broadcastEvent:eventName data:data responder:subview];
}
}
} else if([responder isKindOfClass:[UIView class]]) {
__kindof UIView <JKUIEventProtocol>*view = (__kindof UIView <JKUIEventProtocol>*)responder;
if ([view conformsToProtocol:@protocol(JKUIEventProtocol)]
&& [view respondsToSelector:@selector(jk_receiveBroadcastEvent:data:)]) {
JKUIEventResult result = [view jk_receiveBroadcastEvent:eventName data:data];
if (result == JKUIEventResultHandle) {// 事件停止继续广播
return;
}
}
if (view.subviews.count > 0) {
for (__kindof UIView *subview in view.subviews) {
[self broadcastEvent:eventName data:data responder:subview];
}
}
}
}
@end
源码下载
pod集成
pod 'JKUIEventHandler'
欢迎大家多多批评指正,共同进步。
更多干货文章,欢迎大家扫码关注公众号