View布局-subViews 层级布局
1、touch 触摸手势 事件响应链-》hitTestView, 确定第一响应者
2、在不同View上(父子View,平级View),确定第一响应者
3、在同一个View上 ,自定义的UIControl/UIControl/UIControl系统子类(button,slider,segment等)target-action+ Gesture共存
4、在同一个View上,多个Gesture共存
5、UIView(touchesBegin)+UIControl+ Gesture 响应优先级
6、多个Gesture在不同View上:
父ScrollView+子View 手势
父ScrollView+子ScrollView 手势
父NavigationController View(侧滑手势)+子View手势
UIResponder->UIView->UIControl
1, UIResponder
- (void)touchesXXXXX:(NSSet
2, UIGestureRecognizer
@interface UIView : UIResponder
@interface UIView (UIViewGestureRecognizers)
@property(nullable, nonatomic,copy) NSArray<__kindof UIGestureRecognizer *> *gestureRecognizers NS_AVAILABLE_IOS(3_2);
优先级最高响应,并忽略UIControl方法响应;
直接通过UIApplication直接向其派发消息,并且不再向响应者链派发消息。
[UIWindow sendEvent:] ---> [UIGestureEnvironment _updateGesturesForEvent:window:]--->
@interface UIGestureRecognizer (UIGestureRecognizerProtected)
// mirror of the touch-delivery methods on UIResponder
// UIGestureRecognizers aren't in the responder chain, but observe touches hit-tested to their view and their view's subviews
// UIGestureRecognizers receive touches before the view to which the touch was hit-tested
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches API_AVAILABLE(ios(9.1));
state:possible/began/changed/ended/cancelled/failed
enable
cancelsTouchesInView
delaysTouchesBegan
delaysTouchesEnd
allowedTouchTypes
allowedPressTypes
requiresExclusiveTouchType
- UIControl
@interface UIControl : UIView
[UIWindow sendEvent:] ---》 [UIWindow _sendTouchesForEvent:]
会重写父类(UIResponder)的touchesXXXXX方法,可能会直接取消touchesCancelled/
highlighted
tracking
touchInside
selected
enabled
state:normal/highlighted/disabled/selected/focused/application/reserved
touchesBegan-> beginTrackingWithTouch
touchesEnded-> endTrackingWithTouch->target/action
touchesBegan-> beginTrackingWithTouch
touchesMoved-> continueTrackingWithTouch
touchesMoved-> continueTrackingWithTouch
touchesMoved-> continueTrackingWithTouch->pointinside
touchesEnded->pointinside-> endTrackingWithTouch
1, 寻找第一响应者
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//3种状态无法响应事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
//触摸点若不在当前视图上则无法响应事件
if ([self pointInside:point withEvent:event] == NO) return nil;
//从后往前遍历子视图数组
int count = (int)self.subviews.count;
for (int i = count - 1; i >= 0; i--)
{
// 获取子视图
UIView *childView = self.subviews[i];
// 坐标系的转换,把触摸点在当前视图上坐标转换为在子视图上的坐标
CGPoint childP = [self convertPoint:point toView:childView];
//询问子视图层级中的最佳响应视图
UIView *fitView = [childView hitTest:childP withEvent:event];
if (fitView)
{
//如果子视图中有更合适的就返回
return fitView;
}
}
//没有在子视图中找到更合适的响应视图,那么自身就是最合适的
return self;
}
3, 事件是怎么传递的
事件 是怎么接 收的??
UIReponder 通过 TouchesXXXX方法接收 事件
UIGesture 也是 通过 TouchesXXXX方法接收 事件
UIControl的 Tracking 系列方法是在 touch 系列方法内部调用的。比如 beginTrackingWithTouch 是在 touchesBegan 方法内部调用的
Window怎么知道要把事件传递给哪些手势识别器?
Application怎么知道要把event传递给哪个Window,以及Window怎么知道要把event传递给哪个hit-tested view的问题,
手势识别器也是一样的,event绑定的touch对象上维护了一个手势识别器数组,里面的手势识别器毫无疑问是在hit-testing的过程中收集的
UIControl会阻止父视图上的手势识别器行为,也就是UIControl处理事件的优先级比UIGestureRecognizer高,
但前提是相比于父视图上的手势识别器。UIControl比其父视图上的手势识别器具有更高的事件响应优先级。
如果UIControl本身添加了手势呢?测试结果:UIGesture执行。uicontroler的target-action不执行。对自定义UIControl,UIControl的系统子类(button,slider等)一样。
准确地说只适用于系统提供的有默认action操作的UIControl,
例如UIbutton、UISwitch等的单击,而对于自定义的UIControl,经验证,响应优先级比手势识别器低(父子view上,UIGesture的优先关系)
Window先将事件传递给这些手势识别器,再传给hit-tested view。
一旦有手势识别器成功识别了手势,Application就会取消hit-tested view对事件的响应。
UIResponder响应触摸事件
UIResponder、UIGestureRecognizer、UIControl,笼统地讲,事件响应优先级依次递增。这句话不对啊, 如果是父子view,uigesture最高。如果同一个view上,uibutton的target-action最高。自定义uicontrol低与uigesture
如果多个UIGesture呢(同一个单View上的多个Gesture。父子View的各自的Gesutre)
最后添加的有效
问题1 , 父子view
首先确定 hit- test View
UIGestureRecognizer是默认往super view传递的
1、 这个view没有添加gesture, 看他的父view有没有添加手势, 如果添加了, 父视图的手势方法响应
2、 如果view添加了gesture, 父视图也添加了gesture, 子 view的优先响应, view的不满足条件激活条件的话, 父视图的响应
UIResponder的, 也会往父视图传
1、 view的touch执行了,
2、 super view的touch执行了
3、如果view重写toucheXXXX方法, 不调用super的toucheXXXX, 就在这个View终止了, 事件吸收了
问题2 , 父子view/平级view/同一个view,即添加了UIGesture,又add了target-action,哪个方法执行?
父子view,
父view上有UIGesture,
子view,如果是uibutton,uibutton的点击执行。
子view,如果是UIControl或者自定义UIControl,父view的UIGesture方法执行。如果想要修改,百度一下UIGesture的cancelsTouchesInView是干嘛的,这里懒得bb了,反正我也不懂
同一个view
view上即添加UIGesture又add了target-action,
无论view是UIButton/UIControl还是自定义UIControl子类,view上的UIGesture方法执行。
平级view
一个view在前面一个view在后面,但是他们是平级的。遵循响应链机制,hittest-view第一响应者view上的事件执行
https://www.jianshu.com/p/df86508e2811
测试代码:
- (void)viewDidLoad {
[super viewDidLoad];
//[self testSameView];
[self testSubview];
//[self testneighborView];
}
- (void)testSameView {
double x = 20;
double y = 20;
double width = 100;
double height = 50;
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(x, y, width, height);
btn.backgroundColor = [UIColor redColor];
[btn setTitle:@"btn" forState:UIControlStateNormal];
[self.view addSubview:btn];
y += 70;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[btn addGestureRecognizer:tap];
}
{
UIControl *btn = [UIControl new];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(x, y, width, height);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
y += 70;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[btn addGestureRecognizer:tap];
}
{
CustomControl *btn = [CustomControl new];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(x, y, width, height);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
y += 70;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[btn addGestureRecognizer:tap];
}
}
- (void)btnAction {
NSLog(@"target-action");
}
- (void)tapAction {
NSLog(@"tap called");
}
- (void)testSubview {
double x = 20;
double y = 20;
double width = 100;
double height = 50;
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(0, 0, width, height);
btn.backgroundColor = [UIColor redColor];
[btn setTitle:@"btn" forState:UIControlStateNormal];
UIView *view = [UIView new];
view.frame = CGRectMake(x, y, width + 50, height + 50);
view.backgroundColor = [UIColor grayColor];
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[view addGestureRecognizer:tap];
[view addSubview:btn];
[self.view addSubview:view];
y += 120;
}
{
UIControl *btn = [UIControl new];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(0, 0, width, height);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
UIView *view = [UIView new];
view.frame = CGRectMake(x, y, width + 50, height + 50);
view.backgroundColor = [UIColor grayColor];
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[view addGestureRecognizer:tap];
[view addSubview:btn];
[self.view addSubview:view];
y += 120;
}
{
CustomControl *btn = [CustomControl new];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(0, 0, width, height);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
UIView *view = [UIView new];
view.frame = CGRectMake(x, y, width + 50, height + 50);
view.backgroundColor = [UIColor grayColor];
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[view addGestureRecognizer:tap];
[view addSubview:btn];
[self.view addSubview:view];
y += 120;
}
}
- (void)testneighborView {
double x = 20;
double y = 20;
double width = 100;
double height = 50;
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(x, y, width, height);
btn.backgroundColor = [UIColor redColor];
[btn setTitle:@"btn" forState:UIControlStateNormal];
UIView *view = [UIView new];
view.frame = CGRectMake(x, y, width + 50, height + 50);
view.backgroundColor = [UIColor grayColor];
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[view addGestureRecognizer:tap];
[self.view addSubview:view];
[self.view addSubview:btn];
y += 120;
}
{
UIControl *btn = [UIControl new];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(x, y, width, height);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
UIView *view = [UIView new];
view.frame = CGRectMake(x, y, width + 50, height + 50);
view.backgroundColor = [UIColor grayColor];
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[view addGestureRecognizer:tap];
[self.view addSubview:view];
[self.view addSubview:btn];
y += 120;
}
{
CustomControl *btn = [CustomControl new];
[btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
btn.frame = CGRectMake(x, y, width, height);
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
UIView *view = [UIView new];
view.frame = CGRectMake(x, y, width + 50, height + 50);
view.backgroundColor = [UIColor grayColor];
view.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[view addGestureRecognizer:tap];
[self.view addSubview:view];
[self.view addSubview:btn];
y += 120;
}
}