iOS UI事件的传递机制
2019-04-04 本文已影响4人
飞不越疯人院
日常开发中用到事件传递的场景:
自定义的Tabbar的中间按钮为大的圆形; 预期效果是只让红框内的圆形部分相应点击事件;
文章讲解内容代码示例
现在的效果是点击了非圆形区域也相应了事件,这明显不是想要的效果 image.png
处理后的效果
image.png事件传递的主要两个方法:
///最终哪个视图响应事件, 将哪个视图返回;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.hidden == YES || self.alpha < 0.01 || self.isUserInteractionEnabled == NO) {//0.01临界值
return nil;
}
if ([self pointInside:point withEvent:event]) {
__block UIView *view = nil;
///遍历当前视图的subviews
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
///转换坐标
CGPoint thePoint = [self convertPoint:point toView:obj];
///子视图调用
view = [obj hitTest:thePoint withEvent:event];
if (view) {
*stop = YES;
}
}];
if (view != nil) {
return view;
}else {
return self;
}
}else {
///点击区域不在当前视图范围内, 不要响应
return nil;
}
}
///判断某个点击位置是否在当前视图内;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGFloat x = point.x;
CGFloat y = point.y;
CGFloat centerX = self.center.x;
CGFloat centerY = self.center.y;
///点击点与当前控件中心点距离
double twoPointOffset = sqrt(pow((x - centerX), 2) + pow(y - centerY, 2));
///判断是否在圆形区域内
if (twoPointOffset <= self.frame.size.width / 2) {
return YES;
}
return NO;
}
事件的传递流程:
知识点:倒序遍历, 因为不考虑透明度和frame的因素, 后面添加的会在最上层,理论上也是最先响应;
视图响应流程:
用到的几个方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
如果点击了View5. 则处理事件流程为:
知识点:如果到了UIApplicationDelegate这一层事件仍然没有被响应处理掉,会出现什么现象?
注意, 程序不会崩溃, 而是会将这个事件丢弃掉;
其他:UIView和Calyer的关系
UIView | CALayer |
---|---|
可以响应事件 | 不能响应事件 |
UIView 是Layer 的delegate
|
-- |
负责处理事件 | 负责绘制 |
内部有subviews
|
内部有subLayers
|
这种分工方式体现出单一职责原则
某种程度上简单理解为:CALayer+响应链/事件 = UIView;