IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS 事件响应链 -> 正文阅读

[移动开发]iOS 事件响应链

1.事件的分发

在iOS中, 只有继承了UIResponder的类的对象才能接收并处理事件,我们称为响应者对象
UIApplication,UIViewController,UIView均为响应者对象, 都能够接收并处理事件。事件的分发,只会在他们之间进行。
默认的事件分发的过程
当用户点击了一个UIResponder:
1.系统会将此次触发事件加入到一个由UIApplication管理的队列事件中
2.UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常会先发送事件给应用程序的主窗口(keyWindow)
3.主窗口会判断能否处理事件,如果可以,开始在自己的根View上寻找处理事件的View
4.根View在自己的子View上找处理事件的子VIew(从最后一个添加的子View开始处理),如果找到,事件就交由他处理,响应链终止,如果没有就自己处理响应事件。可以处理事件的VIew的判断满足两点:该view能否处理事件;点击事件在VIew区域内。
5.反复步骤4,如果没有最适合处理事件的VIew就将事件交给KeyWindow处理

2.响应链传递相关函数

1.hitTest:withEvent:

   只要事件一传递给一个控件,这个控件就会调用他自己的hitTest:withEvent:方法寻找处理事件的View,返回的就是处理事件的View
底层具体实现如下 :
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 当前view能否接收事件(如果他能接听,那么就一定是当前view或者他的子view处理掉这个事件,而不会继续向外抛出事件了)
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
        return nil;
    }
    // 判断点在不在当前view范围内
    if ([self pointInside:point withEvent:event] == NO) { 
        return nil;
    }
    // 从后往前遍历子view(后加的子view优先响应,因为他在更上层)
    NSInteger count = self.subviews.count;
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[i];
        // 把当view上的坐标系转换成子view上的坐标系
        CGPoint childPoint = [self convertPoint:point toView:childView];
        UIView *hitView = [childView hitTest:childPoint withEvent:event];
        if (hitView) { // 有可响应的view
            return hitView;
        }
    }
    // 检查子view没有可以响应的,那么就让自己响应
    return self;
}

事件传递给窗口或控件的后,就调用hitTest:withEvent:方法寻找更合适的view。所以是,先传递事件,再根据事件在自己身上找更合适的view。不管子控件是不是最合适的view,系统默认都要先把事件传递给子控件,经过子控件调用自己的hitTest:withEvent:方法验证后才知道有没有更合适的view。即便父控件是最合适的view了,子控件的hitTest:withEvent:方法还是会调用,不然怎么知道有没有更合适的!即,如果确定最终父控件是最合适的view,那么该父控件的子控件的hitTest:withEvent:方法也是会被调用的。
hitTest:withEvent:方法忽略隐藏(hidden=YES)的视图,禁止用户操作(userInteractionEnabled=YES)的视图,以及alpha级别小于0.01(alpha<0.01)的视图。
如果一个子视图的区域超过父视图的bound区域(父视图的clipsToBounds 属性为NO,这样超过父视图bound区域的子视图内容也会显示),那么正常情况下对子视图在父视图之外区域的触摸操作不会被识别,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。

pointInside: withEvent:

该方法判断触摸点是否在控件身上, 是则返回YES, 否则返回NO. 当点击事件在view范围内,默认返回YES

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; 

3.相对位置转换函数

将像素point由point所在视图转换到目标视图view中,返回在目标视图view中的像素值

- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;

将像素point从view中转换到当前视图中,返回在当前视图中的像素值

- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;

将rect由rect所在视图转换到目标视图view中,返回在目标视图view中的rect

- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view;

将rect从view中转换到当前视图中,返回在当前视图中的rect

- (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;

3.响应链使用举例

因为点击事件的传递,就是由控件能否处理事件和点击位置是否为处理位置决定,所以重写以上两个函数,可以实现:
点击视图1,由视图2来响应
让子控件位于父控件之外的部分,响应事件

1.点击视图1由视图2来响应

view1 和view2为同一父视图下不重叠的两个子视图

//view1 中的代码
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    return _view2;
}

2.让子视图位于父视图之外的部分响应事件

重写父view 的hitTest: withEvent: 函数

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    for(int i=0;i<self.subviews.count;i++){
        UIView *view = self.subviews[i];
        CGPoint subPoint = [self convertPoint:point toView:view];
        if([view pointInside:subPoint withEvent:event]){
            return view;
        }
    }
    return self;
}

4.其他

为什么手势和单击事件只会响应手势?

UIGestureRecognizer 有个属性cancelsTouchesInView,这个属性默认值是YES,即当手势识别成功后,会发送touchesCancelled消息给view来结束view的响应。
如果cancelsTouchesInView为NO,那么gestureRecognizer和view都可以响应

UIGestureRecognizer 有个属性cancelsTouchesInView,这个属性默认值是YES,即当手势识别成功后,会发送touchesCancelled消息给view来结束view的响应。

UIView不能接收触摸事件的三种情况

不接受用户交互:userInteractionEnabled = NO;
隐藏:hidden = YES;
透明:alpha = 0.0~0.01

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:57:45  更:2022-07-03 10:58:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 15:06:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码