iOS中的事件

在iOS中不是所有對象都能處理事件,只有繼承了UIResponder的對象才能處理事件,我們稱之為"響應者對象"

事件分類

  • 觸摸事件
  • 加速計事件
  • 遠程控制事件

觸摸事件

// 系統自動調用
- (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;
- (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);

加速計事件

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

遠程控制事件

- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);

UITouch

  • 一個手指對應一個UITouch對象
  • UITouch的作用
  1. 保存跟手指相關的信心,比如觸摸的位置,事件,階段
  2. 當手指移動時,系統會一直更新該UITouch對象,使之能夠一直保存該手指的觸摸位置
  3. 當手指離開屏幕時,系統會自動銷毀該UITouch對象
  • 獲取手指觸摸位置
UITouch *touch = [touches anyObject];
// 當前觸摸的點
CGPoint currentP = [touch locationInView:self];
// 上一個觸摸的點
CGPoint previousP = [touch previousLocationInView:self];

事件的產生和傳遞

  • 發生觸摸事件后,系統會將該事件加入到一個有UIApplication管理的事件隊列中
  • UIApplication會從事件隊列中取出最前面的事件,并將事件分發下去以便處理.通常,先發送事件給應用程序主窗口(keyWindow)
  • 主窗口會在事件層次結構中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理的第一步
  • 找到合適的視圖控件之后,就會調用該視圖的touches方法來做具體的事件處理

事件傳遞的完整過程

  • 先將事件對象由上往下傳遞,找到最合適的控件來處理這個事件
  • 調用最合適控件的touches方法
  • 如果調用了[super touche..]方法,就會將事件順著響應者鏈條向上傳遞,傳遞給上一個響應者
  1. 先去判斷當前的view是不是控制器的view,如果是,那么它的上一個響應者就是它的控制器
  2. 如果當前的view不是控制器的view,那么它的上一個響應者就是它的父控件
  • 接著會調用上一個響應者的touches方法

控件不能處理事件的原因

  • 如果父控件不能處理事件,那么它的子控件也不能接收處理事件
  • userInteractionEnabled = NO // 不接收用戶交互
  • hidden = YES (當一個控件隱藏的時候,它的子控件也會跟隨父控件一起隱藏)
  • alpha = 0.0~0.01 (當一個控件透明的時候,它的子控件也會跟隨父控件一起透明)

如何找到最適合的控件來處理事件

  • 自己是否能接收觸摸事件
  • 觸摸點是否在自己身上
  • 從后往前遍歷子控件,重復以上兩個步驟
  • 如果沒有符合條件的子控件,那么自己是最合適的

// 作用:去尋找最合適的View
// 什么時候調用:當一個事件傳遞給當前view就會調用該方法
// 返回的是誰,誰就是最適合的view
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    return nil;
}

hitTest方法的底層實現

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // 判斷自己能否處理觸摸事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) {
        return nil;
    }
    
    // 判斷點是否在自己身上
    if (![self pointInside:point withEvent:event]) {
        return nil;
    }
    
    // 從后往前遍歷子控件,重復前兩步操作
    NSInteger count = self.subviews.count;
    for (NSInteger i = count -1; i >= 0; i--) {
        // 取出每一個view
        UIView *childView = self.subviews[i];
        // 轉換坐標系
        CGPoint childP = [self convertPoint:point toView:childView];
        UIView *fitView = [childView hitTest:childP withEvent:event];
        
        // 判斷是否找到合適的view
        if (fitView) {
            return fitView;
        }
    }
    
    // 否則自己就是最合適的view
    return self;
}
// 判斷當前的點在不在調用它的view上
// 調用時間: 在hitTest方法中調用
// point點必須要跟它調用者在同一個坐標系里面
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return YES;
}

UIGestureRecognizer手勢識別

  • UIGestureRecognizer它是一個抽象類
  • UITapGestureRecognizer(敲擊)
  • UIPinchGestureRecognizer(捏合)
// 獲取縮放比例
CGFloat scale = tap.scale;
// 復位操作
[pinch setScale:1];
  • UISwipeGestureRecognizer(輕掃)
  • 分方向direction
UISwipeGestureRecognizerDirectionRight
UISwipeGestureRecognizerDirectionLeft
UISwipeGestureRecognizerDirectionUp  
UISwipeGestureRecognizerDirectionDown
  • UIPanGestureRecognizer(拖拽)
// 相對于最原始的偏移量
CGPoint point = [pan translationInView:view];
// 復位操作
[pan setTranslation:CGPointZero inView:view];
  • UILongPressGestureRecognizer(長按)
  • 長按移動時,會持續調用目標方法
  • 長按手勢分狀態:state
 UIGestureRecognizerStatePossible
 UIGestureRecognizerStateBegan
 UIGestureRecognizerStateChanged
 UIGestureRecognizerStateEnded
 UIGestureRecognizerStateCancelled
 UIGestureRecognizerStateFailed
 UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
  • UIRotationGestureRecognizer(旋轉)
// 獲取縮放角度
CGFloat rotation = tap.rotation;
// 復位操作
[tap setRotation:0];
  • 多個手勢操作
// 該代理方法用來判斷是否同時支持多個手勢操作
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

添加手勢方法

UIView *view = [[UIView alloc] init];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
// 設置代理
tap.delegate = self;
[view addGestureRecognizer:tap];

// 該代理方法可以用來設置可以點擊的區域
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    
    return YES;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 好奇觸摸事件是如何從屏幕轉移到APP內的?困惑于Cell怎么突然不能點擊了?糾結于如何實現這個奇葩響應需求?亦或是...
    Lotheve閱讀 57,791評論 51 602
  • 在iOS開發中經常會涉及到觸摸事件。本想自己總結一下,但是遇到了這篇文章,感覺總結的已經很到位,特此轉載。作者:L...
    WQ_UESTC閱讀 6,081評論 4 26
  • 01-UIView的拖拽 1.ios當中常用的事件分為三種: 1.觸摸事件2.加速計事件3.遠程控制事件 2.什么...
    freemanIT閱讀 406評論 0 0
  • -- iOS事件全面解析 概覽 iPhone的成功很大一部分得益于它多點觸摸的強大功能,喬布斯讓人們認識到手機其實...
    翹楚iOS9閱讀 2,989評論 0 13
  • 響應者對象 在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接收并處理事件。我們稱之...
    JonesCxy閱讀 709評論 0 0