iOS基于响应链以及视图层级结构的事件流动

  由于项目中团队协作,以及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'

欢迎大家多多批评指正,共同进步。

更多干货文章,欢迎大家扫码关注公众号

这里写图片描述

发布了231 篇原创文章 · 获赞 110 · 访问量 60万+

猜你喜欢

转载自blog.csdn.net/HHL110120/article/details/104055352
今日推荐