问题1: 请说一下UIView中事件传递机制
流程图关键方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
-
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
: 返回视图响应事件的视图 -
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
: 点击位置是否在当前视图范围内, 是返回:YES
/ 不是返回:NO
流程说明
-
点击屏幕某一个位置, 那么这个事件其实是传递给
UIApplication
-
UIApplication
传递给UIWindow
, 要留意UIWindow
也是一个视图 -
UIWindow
里面执行- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
去找到相应的视图 -
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法内部调用- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
来判断点击方法是否在范围内- 是: 遍历子视图, 其中遍历方法为
倒序遍历
, 最后添加视图最优先遍历到- 子视图内部调用子视图的
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
即 [sub hitTest: withEvent:], 如果找到返回的子视图hitView
, 那么它作为事件响应视图结束查找。
- 子视图内部调用子视图的
- 否: 子视图没找到会倒序遍历其他子视图, 如果都没找到, 返回当前视图。如果都没找到, 那么将
UIWindow
返回给调用方
- 是: 遍历子视图, 其中遍历方法为
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
内部实现流程图
问题2: 下面的例子你一般怎么处理或实现
(通常带着例子让你解释)
例如
例子上面的按钮只有粉色圆形位置可以点击, 周围黑色部分不能点击, 怎么实现? (即: 按钮指定区域接收响应事件问题)
-
当然我们也可以定义2个视图, 一个处理背景, 一个处理上面按钮达到实现的目的
-
通过重写点击视图方法实现
demo 下载地址放在评论当中
#import "SRButton.h"
// SRButton 是UIButton子类
@implementation SRButton
// 重写方法
// 判断点击是否在当前视图方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 判断当前视图是开启交互, 是否隐藏, 透明度小于0.01
// 这几种情况UIButton 默认是不执行的点击方法的
// 不写的话也可以, 但是举个例子: 透明度0.005也可以点击, 但实际上是看不到按钮的
if (!self.userInteractionEnabled ||
[self isHidden] ||
self.alpha <= 0.01){
// 当前view不响应点击事件
return nil;
}
// 判断点击位置是否在当前视图范围内
if ([self pointInside:point withEvent:event]){
// 设置一个初始视图
__block UIView *hitView = nil;
// 遍历子视图
// NSEnumerationReverse 倒序
[self.subviews enumerateObjectsWithOptions: NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CGPoint p = [self convertPoint:point toView:obj];
hitView = [obj hitTest:p withEvent:event];
if (hitView) {
*stop = YES;
}
}];
if (hitView){
return hitView;
}
return self;
}
// 点击区域不在范围内返回nil
return nil;
}
// 判断当前点击区域是否在范围内
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// 获取当前点坐标
CGFloat x1 = point.x;
CGFloat y1 = point.y;
// 按钮中心点坐标
CGFloat x2 = CGRectGetWidth(self.frame) / 2;
CGFloat y2 = CGRectGetHeight(self.frame) / 2;
// 计算2个点之间的距离
double d = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 判断点击位置是否在范围内, 实际上判断距离中心点的距离
// 20: 是我这边设置个随意的边界差值, 便于观察, 根据自己需求设置
if (d < CGRectGetWidth(self.frame) / 2 - 20){
return YES;
}
// 不在范围内返回no
return NO;
}
例子操作
最好结合流程图看便于理解上面的代码
流程图
问题3: 请说一下UIView响应流程
视图响应链流程图-
UILabel
,UItextFiled
,UIButton
等响应者都是当前的UIView
-
UIView
的响应者是父 UIView
(如果没有依次下顺) -
父UIView
的响应者是UIViewController
(如果没有依次下顺) -
UIViewController
的响应者是UIWindow
(如果没有依次下顺) -
UIWindow
的响应者是UIApplication
最终UIApplicationDelegate
以上就是视图响应或者说传递链的过程
问题4: 表述下面例子的响应流程
问题4响应过程:
- 点击白色位置首先由
View D
是接收响应 -
View D
如果不响应则由直接父视图View B2
去响应 -
View B2
如果不响应则由父视图View A
去响应 -
View A
如果不响应, 则去View A
响应链继续查找直至UIApplicationDelegate
问题4追问: 如果上面都没有响应, 会怎么样
忽略响应事件, App不会做任何响应, 不会crash
网友评论