美文网首页
iOS 响应者链理解

iOS 响应者链理解

作者: summerNight | 来源:发表于2019-02-21 13:46 被阅读0次

    理解

    响应者链主要包含两个部分,一个是事件的传递,另外一个是响应事件

    事件传递

    事件传递.png
    1. 触摸屏幕,产生事件,将事件传递到UIApplication管理的事件队列中
    2. UIApplication将事件传递给UIwindow
    3. UIWindow调用hitTest: withEvent:方法。
      • hitTest: withEvent:UIView的方法,UIWindow是继承UIView,所以也会调用这方法
      • hitTest: withEvent:方法与pointInside: withEvent:总是成对出现,hitTest: withEvent:内部调用pointInside: withEvent:方法,判断触摸点是否在当前视图内
      • 若在当前视图内,就倒序遍历子视图,子视图也是重复上面几步,调用hitTest: withEvent:方法。如果hitTest: withEvent:有返回非空值,那么hitTest: withEvent:就返回这个子视图;如果UIWindow所有子视图的hitTest: withEvent:方法返回都为nil,那么UIWindow就是响应视图

    响应事件

    响应者链.png
    上面这个图是从苹果官方文档复制过来的,通过事件传递,找到适合的响应者成为first responder,即第一响应者,如果第一响应者未处理事件,就会根据响应者链,将事件传递给下一个响应者。UIViewUIViewUIViewController (如果没有UIViewController就直接传给UIWindow ) → UIWindowUIApplicationDelegate。若消息被处理了就默认不继续传递,若所有响应者都不处理,那就不处理了,啥也没发生。

    DEMO

    自定义响应范围的button

    写一个自定义UIButton,点击button圆圈范围内才响应事件,否则不响应。
    .m实现:

    #import "MYButton.h"
    
    @implementation MYButton
    
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
        // 判断是否能响应事件
        if ([self isHidden] ||
            !self.userInteractionEnabled ||
            self.alpha < 0.01) {
            return nil;
        }
        // 不在点击范围
        if (![self pointInside:point withEvent:event]) {
            return nil;
        }
        // 倒序遍历
        __block UIView *view = nil;
        [self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            // 转换相对坐标
            CGPoint convertP = [self convertPoint:point toView:obj];
            view = [obj hitTest:convertP withEvent:event];
            if (view) {
                *stop = YES;
            }
        }];
        if (view) {
            return view;
        }
        else{
            return self;
        }
    }
    
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
        CGFloat x = point.x;
        CGFloat y = point.y;
        CGFloat r = self.bounds.size.width * 0.5;
        CGFloat delta = sqrt((r-x) * (r-x) + (r-y) * (r-y));
        if (delta <= r) {
            return YES;
        }
        else{
            return NO;
        }
    }
    
    

    相关文章

      网友评论

          本文标题:iOS 响应者链理解

          本文链接:https://www.haomeiwen.com/subject/sraxyqtx.html