iOS开发知识

事件传递,响应者链以及手势识别器

2019-08-06  本文已影响0人  太阳骑士索拉尔

关于我的仓库

前言

好多概念

“响应链”(responder chain)

UIEvent

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIEvent : NSObject

@property(nonatomic,readonly) UIEventType     type NS_AVAILABLE_IOS(3_0);
@property(nonatomic,readonly) UIEventSubtype  subtype NS_AVAILABLE_IOS(3_0);

@property(nonatomic,readonly) NSTimeInterval  timestamp;

#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly, nullable) NSSet <UITouch *> *allTouches;
#else

@end
DF79B032-A77C-4F89-9DFE-2E57A96738F4

UITouch

87D4D165-92CF-4F35-96DD-5986C0FCC7E0

UIEvent与UITouch

手势识别器【GESTURE RECOGNIZER

UIView,UIGestureRecognizer以及UIControl

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
typedef NS_OPTIONS(NSUInteger, UIControlEvents) {
    UIControlEventTouchDown                                         
    UIControlEventTouchDownRepeat                                   
    UIControlEventTouchDragInside                                   
    UIControlEventTouchDragOutside                                  
    UIControlEventTouchDragEnter                                    
    UIControlEventTouchDragExit                                     
    UIControlEventTouchUpInside                                     
    UIControlEventTouchUpOutside                                    
    UIControlEventTouchCancel                                       
    UIControlEventValueChanged                                      
    UIControlEventPrimaryActionTriggered NS_ENUM_AVAILABLE_IOS(9_0) 
    UIControlEventEditingDidBegin                                   
    UIControlEventEditingChanged                                    
    UIControlEventEditingDidEnd                                     
    UIControlEventEditingDidEndOnExit                               
    UIControlEventAllTouchEvents                                    
    UIControlEventAllEditingEvents                                  
    UIControlEventApplicationReserved                               
    UIControlEventSystemReserved                                    
    UIControlEventAllEvents                                        
};

响应者链全貌

hit-test

5955953E-1062-4001-839E-48A60B9B2DCE
  1. 触摸点在视图 A 的区域范围内,然后开始检查子视图 B 和 C

  2. 触摸点不在 B 的范围而在 C 的范围,于是就开始检查 D 和 E 视图

  3. 触摸点不在 D 的范围而在 E 的范围,而 E 视图是视图树最底层的并包含触摸点的

    视图对象,所以 E 就成为了 hit-test 视图。

小实验:hittest调用顺序

CF9A1FF4-9776-4D7D-AECE-F9423B46C77D
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01 || ![self pointInside:point withEvent:event]) {
        return nil;
    } else {
        for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
            NSLog(@"当前hsubView:%p 当前所在View:%p", subview, self);
            UIView *hitView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event];
            if (hitView) {
                NSLog(@"确认hit-TestView:%p 当前所在View:%p", hitView, self);
                return hitView;
            }
        }
        return self;
    }
}
//我们在里面添加其打印,方便我们验证这个流程
//userInteractionEnabled属性为YES,该属性表示允许控件同用户交互。
//Hidden属性为NO。控件都看不见,自然不存在触摸
//opacity属性值0 ~0.01。
//触摸点在这个UIView的范围内。
//在最开始直接打印每个View的地址
viewA:0x7fddea408c70, viewB:0x7fddea4061f0, viewC:0x7fddea40b160, viewD:0x7fddea40e090, viewE:0x7fddea40e480

//点击viewD
当前hsubView:0x7fddea40b160 viewC 当前所在View:0x7fddea408c70 viewA
当前hsubView:0x7fddea40e480 viewE 当前所在View:0x7fddea40b160 viewC
当前hsubView:0x7fddea40e090 viewD 当前所在View:0x7fddea40b160 viewC
确认hit-TestView:0x7fddea40e090 viewD 当前所在View:0x7fddea40b160 viewC
确认hit-TestView:0x7fddea40e090 viewD 当前所在View:0x7fddea408c70 viewA
  
当前hsubView:0x7fddea40b160 viewC 当前所在View:0x7fddea408c70 viewA
当前hsubView:0x7fddea40e480 viewE 当前所在View:0x7fddea40b160 viewC
当前hsubView:0x7fddea40e090 viewD 当前所在View:0x7fddea40b160 viewC
确认hit-TestView:0x7fddea40e090 viewD 当前所在View:0x7fddea40b160 viewC
确认hit-TestView:0x7fddea40e090 viewD 当前所在View:0x7fddea408c70 viewA
  
//点击viewE
当前hsubView:0x7fddea40b160 当前所在View:0x7fddea408c70
当前hsubView:0x7fddea40e480 当前所在View:0x7fddea40b160
确认hit-TestView:0x7fddea40e480 当前所在View:0x7fddea40b160
确认hit-TestView:0x7fddea40e480 当前所在View:0x7fddea408c70
 
当前hsubView:0x7fddea40b160 当前所在View:0x7fddea408c70
当前hsubView:0x7fddea40e480 当前所在View:0x7fddea40b160
确认hit-TestView:0x7fddea40e480 当前所在View:0x7fddea40b160
确认hit-TestView:0x7fddea40e480 当前所在View:0x7fddea408c70

hittest调用顺序

问题:为什么我们的hittest走了两次,打印了两遍?

总结流程

img img

事件响应

BF9E55A3-A047-4B47-871B-9F68C48C3658

实验

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"Touch:%p", self);
    [super touchesBegan:touches withEvent:event];
}
viewA:0x7fdc664061f0, viewB:0x7fdc664072f0, viewC:0x7fdc664074d0, viewD:0x7fdc664076b0, viewE:0x7fdc66407aa0

//点击viewD
当前hsubView:0x7fdc664074d0 当前所在View:0x7fdc664061f0
当前hsubView:0x7fdc66407aa0 当前所在View:0x7fdc664074d0
当前hsubView:0x7fdc664076b0 当前所在View:0x7fdc664074d0
确认hit-TestView:0x7fdc664076b0 当前所在View:0x7fdc664074d0
确认hit-TestView:0x7fdc664076b0 当前所在View:0x7fdc664061f0
当前hsubView:0x7fdc664074d0 当前所在View:0x7fdc664061f0
当前hsubView:0x7fdc66407aa0 当前所在View:0x7fdc664074d0
当前hsubView:0x7fdc664076b0 当前所在View:0x7fdc664074d0
确认hit-TestView:0x7fdc664076b0 当前所在View:0x7fdc664074d0
确认hit-TestView:0x7fdc664076b0 当前所在View:0x7fdc664061f0
Touch:0x7fdc664076b0 viewD
Touch:0x7fdc664074d0 viewC
Touch:0x7fdc664061f0 viewB

思考

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"viewContrller");
}

button的奇妙冒险

90130DE8-599E-4A1A-8AA1-CDC5ABFF3105 F996501B-478E-49EC-813D-8879FCBF5E95

键盘收回

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}
//点击键盘上的return,收回键盘
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    // 必须辞去第一响应者后,键盘才会回缩.
    [textField resignFirstResponder];
    return YES;
}

第一响应器

小实验:这就是我的触发路线,第一响应者!

[viewC becomeFirstResponder];

- (BOOL)canBecomeFirstResponder {
    return YES;
}

手势控制器

page22image4374528.png

CALayer的hitTest方法

- (CALayer *)hitTest:(CGPoint)thePoint

//Returns the farthest descendant of the receiver in the layer hierarchy (including itself) that contains a specified point.

異議あり!

11DD55685B256F707DB1501410FB6F5D

Q&A

Q:UIView默认不会响应事件?

A:UIView等控件都继承于UIResponder,只要继承于UIResponder的都是响应者,绝壁都能响应事件。

Q:UIView没有实现touchesBegan方法?

A:。。。touches四兄弟都是UIResponder里的方法,只要继承了,肯定就实现了???【感觉不太聪明的亚子】

Q:我丢,照你这么说这么理解呀!

A:其实问题在于你怎么理解响应事件。翻译下这位大神想要表示的意思:UIControl在响应了事件后,会终止响应链,不再往下传。事实确实如此,但对于UIView,其实每一个都响应接收到事件了,只是它没有进行相应的处理,直接往下传了而已。

参考文章

赞美太阳!

上一篇下一篇

猜你喜欢

热点阅读