UI技术总结--UI事件传递&响应

在讲述UI事件传递以前,先要知道UIView 和 UILayer的区别是什么.简而言之bash

  • UIView为其提供内容,以及负责处理触摸等事件,参与响应链
  • CALayer负责显示内容contents UIView只负责事件传递和视图响应链,而显示部分的内容都是由CALayer来负责, 这提现了系统设计UIView和CALayer中所运用的一个设计原则,就是单一职责原则.
接下来看看这幅图

  • 思考一下,当点击图中白圈那个位置的时候,事件是如何进行传递的呢.

事件的传递主要和两个方法有关ide

// 返回哪一个视图响应这个事件,返回nil表示不处理该事件
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判断某一个点击的位置是否在视图范围内
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
复制代码

接下来来看看这个流程图 ui

首先,先判断视图的用户交互是否打开、是否隐藏、透明度是否大于0.01,若是有一个不符合,那么该视图将不处理任何事件响应,也不会进行后续的操做了.若是这三个条件都知足了,就会调用当前视图的-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event方法来判断点击的点是否在当前视图范围内,若是不在的话也会返回nil再由它当前视图的父视图去遍历它的同级兄弟视图,调用对应的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法.若是当前视图的-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event返回YES,那么就会以倒叙的方式遍历当前视图的子视图,遍历的过程当中会调用全部自视图的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法,若是某个子视图返回了最终的事件响应视图的话,就会把对应的视图做为最终的响应视图返回给调用方,若是返回的是nil的话就会继续遍历当前视图的下一个子视图.若是所有遍历完了以后都没有对应的子视图去响应事件的话,因为当前点击位置在当前视图范围内,就会把当前的视图做为响应视图返回给调用方.spa

经过一个例子来更深刻的理解一下

在下图这个正方形按钮中,只有白色圆型区域能够响应点击事件. 设计

自定义一个button,经过 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event方法来实现上诉功能.

#import "CustomButton.h"

@implementation CustomButton

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (!self.userInteractionEnabled ||
        [self isHidden] ||
        self.alpha <= 0.01) {
        return nil;
    }
    // 若是点击区域在白色圆形区域内,则返回self,不然不响应事件
    if ([self pointInside:point withEvent:event]) {
            return self;
    }
    else{
        return nil;
    }
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGFloat x1 = point.x;
    CGFloat y1 = point.y;
    
    CGFloat x2 = self.frame.size.width / 2;
    CGFloat y2 = self.frame.size.height / 2;
    // 这就是传说中的勾股定理
    double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    if (dis <= self.frame.size.width / 2) {
        return YES;
    }
    else{
        return NO;
    }
}

@end

复制代码

好了,如今这个自定义的button就实现了上述的功能了. 回到最开始的问题,点击那个白色位置,事件是如何进行传递的呢.以下图: code

首先点击位置在C二、B二、和A区域内,那么这3个视图都是有可能响应事件的.若是C2是B2的子视图,B2是A的子视图的话,那么按照顺序应该先调用子视图的-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法,而若是他们三个视图是平级的话,按照同级视图倒叙遍历的方式,也是C2先响应(由于C2覆盖在B2和A上).因此顺序就是C2先执行-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法若是返回nil,B2再执行,最后才是A.若是都没有处理这个事件,那么最后会传到UIApplication,若是仍然没有处理这个事件,那么最后就会忽略这个事件.cdn

终.

相关文章
相关标签/搜索