1 事件产生与传递
目的:找到可能的处理事件的Responder。传递顺序,依赖于视图树
,从树根到树叶。
1.1 基于UIResponder的hitTest & pointInside
事件传递参考这2篇文章,一篇搞定事件传递、响应者链条、hitTest和pointInside的使用
iOS 事件传递与响应链
简单概括下,
- hitTest是看自己+subviews能不能响应。
2. pointInside是hitTest的子过程。
3. 满足(1)-(4),才看自己的subviews能不能响应。
以下六种情况中,hitTest返回nil。
(1)[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
(2)不接收用户交互 userInteractionEnabled = NO
(3)隐藏 hidden = YES
(4)透明 alpha = 0.0 ~ 0.01
(5)pointInside返回No
(6)自己的所有subview的hitTest也返回nil
subview的访问顺序,是从数组末尾往前找,也就是从离用户最近的view开始。
2 响应链,由小到大
上一节中,已经找到了最合适的UIResponder,从他开始,通过nextResponder,找出最终处理事件的UIResponder。
一个
例子
就是TableView能响应滚动(PanGesture),cell上有一些元素能响应click(如新闻收藏、点赞、打开)。如果确实是Pan,虽然PanGesutre的Responder是在下方,但仍然是最终处理的事件仍然是Pan,Responder是TableView。
2.1 先手势,再UIControl,最后UIResponder
手势的响应
,继承于UIControl的控件
,继承于UIScroll的控件
在识别出用户操作后,都会结束响应者链,不再往上传递
- UIControl可以处理的事件,如:UIButton的UITouchUpInside等,这些事件实际上是对UITouchBegan的封装,所以优先级高于UITouchBegan
2. UIResponder的时间,如:UIView的UITouchBegan等
3 一些应用
3.1 键盘消失
1. self.view上加Tap手势
2. scrollView的手动上下滑
3. viewWillDisappear
4. 其他特定需求
3.2 Panel消失
panel处于一个透明(半透明)view上,这个view上加点击事件。
3.3 UIView添加Tap后,防止UITableViewCell上的点击失效
- (void)viewDidLoad {
//...
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)];
gesture.delegate = self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UITableView class]]) {
return NO;
}
if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) {
return NO;
}
return YES;
}
3.4 扩大button响应范围
与pointInside、hittest有关
3.5 没有clip时,点在超出的subview上也不会响应
[图片上传失败...(image-bbb9a-1560185032590)]
原理就是事件传递的方式。
同理,父视图userInteractionEnabled设置为No后,子视图就收不到事件了。
3.6 防止短时间内连续点击
原理:hook UIControl的sendAction方法。做一个短时失效的处理。
#import "UIControl+Limit.h"
#import
static const char *UIControl_acceptEventInterval="UIControl_acceptEventInterval";
static const char *UIControl_ignoreEvent="UIControl_ignoreEvent";
@implementation UIControl (Limit)
#pragma mark - acceptEventInterval
- (void)setAcceptEventInterval:(NSTimeInterval)acceptEventInterval {
objc_setAssociatedObject(self,UIControl_acceptEventInterval, @(acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSTimeInterval)acceptEventInterval {
return [objc_getAssociatedObject(self,UIControl_acceptEventInterval) doubleValue];
}
#pragma mark - ignoreEvent
-(void)setIgnoreEvent:(BOOL)ignoreEvent {
objc_setAssociatedObject(self,UIControl_ignoreEvent, @(ignoreEvent), OBJC_ASSOCIATION_ASSIGN);
}
-(BOOL)ignoreEvent {
return [objc_getAssociatedObject(self,UIControl_ignoreEvent) boolValue];
}
#pragma mark - Swizzling
+(void)load {
Method a = class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));
Method b = class_getInstanceMethod(self,@selector(swizzled_sendAction:to:forEvent:));
method_exchangeImplementations(a, b);//交换方法
}
- (void)swizzled_sendAction:(SEL)action to:(id)target forEvent:(UIEvent*)event {
if(self.ignoreEvent){
NSLog(@"btnAction is intercepted");
return;
}
if(self.acceptEventInterval>0) {
self.ignoreEvent=YES;
[self performSelector:@selector(setIgnoreEventWithNo) withObject:nil afterDelay:self.acceptEventInterval];
}
[self swizzled_sendAction:action to:target forEvent:event];
}
-(void)setIgnoreEventWithNo {
self.ignoreEvent=NO;
}
@end
3.7 自定义导航栏后,左滑失效,解决手势冲突
只能说和navigationController.navigationBar.hidden=YES;
有关
@property (nonatomic, weak)id originalNavigateDelegate;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.originalNavigateDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.interactivePopGestureRecognizer.delegate = self.originalNavigateDelegate;
}
3.8 处理视频进度条、音量;微信语音输入
//PanGestureRecognizer处理视频进度条、音量
//根据state分别处理
//1. Begin和第一次达到阈值的变化,区分滑动类型
//2. 离开前,更新进度条、时间、缩略图
//3. 离开时,更新视频进度
//PanGestureRecognizer处理微信语音输入
//1. 根据位置,更新提示(取消发送、发送)
//2. 根据音量,更新音量条
//3. 同步更新语音识别文字
//4. 离开时,发送