iOS之[事件全解析]

iOS只有繼承UIResponder類的對象才能處理事件。(UIView,UIViewController,UIApplication...)app

UIResponder中定義了上述幾類事件函數

1.觸摸事件

:fa-hand-o-down:一根或多根手指開始觸摸屏幕時開始執行ui

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;

:fa-hand-o-down:一根或多根手指在屏幕上移動時執行,:tw-2757:移動過程當中會屢次調用atom

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;

:fa-hand-o-down:一個或多根手指觸摸結束離開屏幕時執行代理

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;

:fa-hand-o-down:觸摸意外終止時執行(如:觸摸時打入電話)code

- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;

:fa-plus-square:iOS9.1新加方法orm

- (void)touchesEstimatedPropertiesUpdated:(NSSet * _Nonnull)touches(IOS9.1)

能夠從UITouch對象中,獲取一下觸摸信息:對象

//取得一個觸摸對象,(對於多點觸摸可能有多個對象) UITouch *touch = [touches anyObject];繼承

touch.window : 觸摸時所在的窗口three

touch.view : 觸摸時所在的視圖

touch.tapCount : 短期內的點擊次數

touch.timestamp : 觸摸產生或變化的時間戳

touch.phase : 觸摸週期內的各個狀態

locationInView: 方法:取得在指定視圖的位置

previousLocationInView : 方法:取得移動前的一個位置

...

......

2.按壓事件(IOS9)

:fa-hand-o-down:按壓物理按鈕時開始執行

- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event;

:fa-hand-o-down:按壓物理按鈕時開始執行並移動時開始執行,:tw-2757:移動過程當中會屢次調用

- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event;

:fa-hand-o-down:按壓物理按鈕結束,離開屏幕時開始執行

- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event;

:fa-hand-o-down:按壓意外終止時執行(如:按壓時打入電話)

- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event;

3.運動事件

**前提:**監聽運動事件時,監聽對象必須是第一響應者(UIViewController,UIApplication無此要求)

①-(BOOL)canBecomeFirstResponder{
        return YES;
    }
    ②視圖顯示時,註冊爲第一響應者
    -(void)viewWillAppear:(BOOL)animated{
        [view becomeFirstResponder];
    }
    ③視圖再也不顯示時,註銷第一響應者
    -(void)viewWillDisappear:(BOOL)animated{
        [view resignFirstResponder];
    }

API:

:fa-hand-o-down:運動開始時執行

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event ;

:fa-hand-o-down:運動結束時執行

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event ;

:fa-hand-o-down:運動意外取消時執行

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;

4.遠程控制事件

主要說耳機線控

遠程控制事件通常都添加到UIApplication,UIViewController中,不會直接添加到UI控件

前提:

①啓動遠程事件接受
       [[UIApplication shareApplication] beginReceivingRemoteControlEvents]; 

    ②UI控件必須爲第一響應者(UIViewController UIApplication對象監聽無此要求)
        [view becomeFirstResponder];

    ③應用程序必須是當前音頻的控制者:通知欄中的音頻播放程序必須是本身開發的程序

事件類型:

搖晃事件+(遠程控制:線控)音頻播放
typedef NS_ENUM(NSInteger, UIEventSubtype) {

    UIEventSubtypeNone                              = 0, 
    
    UIEventSubtypeMotionShake                       = 1,   //>>搖晃(運動)事件
    
    UIEventSubtypeRemoteControlPlay                 = 100, //>>播放:耳機線控中間按鈕按一下
    UIEventSubtypeRemoteControlPause                = 101, //>>暫停
    UIEventSubtypeRemoteControlStop                 = 102, //>>中止
    UIEventSubtypeRemoteControlTogglePlayPause      = 103, //>>播放暫停切換:耳機線控中間按鈕按一下
    UIEventSubtypeRemoteControlNextTrack            = 104, //>>下一曲:耳機線控中間按鈕按兩下
    UIEventSubtypeRemoteControlPreviousTrack        = 105, //>>上一曲:耳機線控中間按鈕按三下
    UIEventSubtypeRemoteControlBeginSeekingBackward = 106, //>>快退開始:耳機線控中間按鈕按三下不要鬆開
    UIEventSubtypeRemoteControlEndSeekingBackward   = 107, //>>快退結束:耳機線控中間按鈕按三下到了快退位置鬆開
    UIEventSubtypeRemoteControlBeginSeekingForward  = 108, //>>快進開始:耳機線控中間按鈕按兩下不要鬆開
    UIEventSubtypeRemoteControlEndSeekingForward    = 109, //>>快進結束:耳機線控中間按鈕按兩下到了快進位置鬆開
};

事件觸發函數

- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

5.手勢識別

離散手勢:一旦識別沒法取消,且只會觸發一次手勢操做事件

連續手勢:識別後能夠取消, 會屢次觸發手勢操做事件

5.1IOS中手勢分爲如下六種:
UIGestureRecognizer
    |-屬性
    |   |-@property(nonatomic, readonly) UIGestureRecognizerState state 手勢狀態
    |   |-@property(nonatomic, getter=isEnabled) BOOL enabled          手勢是否可用
    |   |-@property(nullable, nonatomic,readonly) UIView *view       觸發手勢的視圖
    |   |-@property(nonatomic) BOOL delaysTouchesBegan  //default:NO
    |   |                        |-YES:成功識別手勢,則不執行觸摸開始事件,失敗則執行
    |   |                        |-NO :無論是否識別手勢,都執行觸摸開始事件
    |   |-@property(nonatomic) BOOL delaysTouchesEnded;
    |——————————————————————————————————————————————————————————————————
    |- 方法
    |   |-(void)addTarget:(id)target action:(SEL)action;   //添加觸摸執行事件
    |   |-(void)removeTarget:(id)target action:(SEL)action;//移除觸摸執行事件
    |   |-(NSUInteger)numberOfTouches;                     //同時觸摸的手指數   
    |   |-(CGPoint)locationInView:(UIView*)view;           //單點觸摸在指定視圖中的相對位置
    |   |-(CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(UIView*)view; //多點觸摸時觸摸點在視圖中的相對位置
    |   |-(void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer; //手勢失敗時,執行另外一個手勢
    |——————————————————————————————————————————————————————————————————
    |- 代理方法:UIGestureRecognizerDelegate
    |   |-(待更新)
    |   |
    |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    |-六種子類手勢
    |   |-UITapGestureRecognizer        -點擊手勢(離散)
    |   |   |-@property (nonatomic) NSUInteger  numberOfTapsRequired //點擊次數:1
    |   |   |-@property (nonatomic) NSUInteger  numberOfTouchesRequired //點擊手指數:1
    |   |————————————————————————————————————————————————————————————
    |   |-UILongPressGestureRecognizer  -長按手勢
    |   |   |-@property (nonatomic) NSUInteger  numberOfTapsRequired:1
    |   |   |-@property (nonatomic) NSUInteger  numberOfTouchesRequired:1
    |   |   |-@property (nonatomic) CFTimeInterval minimumPressDuration //設置長按時間 默認:0.5
    |   |   |-@property (nonatomic) CGFloat allowableMovement //默認容許移動10個像素
    |   |————————————————————————————————————————————————————————————
    |   |-UIPinchGestureRecognizer      -捏合手勢
    |   |   |-@property (nonatomic)          CGFloat scale   //紀錄縮放比例
    |   |   |-@property (nonatomic,readonly) CGFloat velocity//紀錄縮放速度
    |   |————————————————————————————————————————————————————————————
    |   |-UIPanGestureRecognizer        -拖動手勢
    |   |   |-@property (nonatomic) NSUInteger minimumNumberOfTouches //手勢所需最小手指數
    |   |   |-@property (nonatomic) NSUInteger maximumNumberOfTouches //手勢所需最大手指數
    |   |   |-(CGPoint)translationInView:(UIView *)view //移動距離
    |   |   |-(CGPoint)velocityInView:(UIView *)view    //移動速度
    |   |————————————————————————————————————————————————————————————
    |   |-UISwipeGestureRecognizer      -輕掃手勢:支持四個方向
    |   |   |-@property(nonatomic) NSUInteger numberOfTouchesRequired //:1
    |   |   |-@property(nonatomic) UISwipeGestureRecognizerDirection direction
    |   |————————————————————————————————————————————————————————————
    |   |-UIRotationGestureRecognizer   -旋轉手勢
    |   |   |-@property (nonatomic)          CGFloat rotation  //紀錄旋轉角度
    |   |   |-@property (nonatomic,readonly) CGFloat velocity  //紀錄旋轉速度
5.2手勢狀態:
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
    UIGestureRecognizerStatePossible,   //>>默認,還沒有識別
    UIGestureRecognizerStateBegan,      //>>手勢開始,已被識別(瞬間)
    UIGestureRecognizerStateChanged,    //>>手勢狀態發生變化
    UIGestureRecognizerStateEnded,      //>>手勢識別操做完成(已鬆開手指)
    UIGestureRecognizerStateCancelled,  //>>手勢被取消,恢復默認
    UIGestureRecognizerStateFailed,     //>>手勢識別失敗,恢復默認
    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
5.3如何使用手勢

以旋轉爲例子-分爲如下幾個步驟:

:tw-26a0:對於非交互UI控件(UIImageView)須要將userInteractionEnabled = NO;

:tw-26a0:輕掃手勢雖然是連續手勢,但:操做事件智慧在識別結束時調用一次。有必要進行狀態判斷

:one:建立對應的手勢對象

UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationImage:)];

:two:設置手勢識別屬性(可選) - :three:附加手勢到指定對象

[_imageView addGestureRecognizer:rotationGesture];

:four:編寫手勢操做方法

//旋轉圖片
    -(void)rotationImage:(UIRotationGestureRecognizer *)gesture{

        if (gesture.state == UIGestureRecognizerStateChanged) {
        
            // 旋轉手勢中rotation記錄了旋轉弧度
            _imageView.transform = CGAffineTransformMakeRotation(gesture.rotation);
        }else if (gesture.state == UIGestureRecognizerStateEnded){
    
            [UIView animateWithDuration:0.8 animations:^{
                //取消形變
                _imageView.transform = CGAffineTransformIdentity;
            }];
        }
    }
5.4手勢衝突

拖動手勢的操做事件是在手勢的開始狀態(狀態1)被識別執行

輕掃手勢的操做事件是在手勢的結束狀態(狀態3)被識別執行

所以輕掃手勢沒有被正確識別

總結: iOS中手勢A的識別部分是手勢B的子部分時,默認A先被識別,B就沒法識別了 利用:

//手勢失敗時,執行另外一個手勢
    - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;

經過該方法:指定拖動手勢執行的前提爲 輕掃手勢失敗。

這樣系統會優先執行輕掃手勢,若是不是輕掃手勢,就會執行拖動 例:

//解決拖動和輕掃的衝突
    [panGesture requiredGestureRecognizerToFail : swipeGestureToRight];    

    //解決拖動和長按手勢的衝突
    [longGesture requiredGestureRecognizerToFail : panGesture];
5.5兩個不一樣控件手勢同時執行

事件觸發事件是根據響應鏈傳播的,上層觸摸事件執行後就不會向下傳播。

手勢也是相似:先識別的手勢會阻斷手勢識別操做繼續傳播

:fa-question-circle:如何讓兩個有層次關係並都添加了手勢的空間正確識別手勢?

:fa-check-square:經過代理UIGestureRecognizerDelegate

vcViewGesture.delegate = self;
    [vc.view addGestureRecognizer : vcViewGesture]; 
    
    //默認返回NO:阻止手勢向下傳播
    //YES :容許向下傳播
    //控制只有在UIImageView中才能向下傳播
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    if([otherGestureRecognizer.view isKindOfClass:[UIImageView class]]){
        return YES;
    }
    return NO;
}
相關文章
相關標籤/搜索