iOS學習筆記(2)— UIView用戶事件響應


 

  

  UIView除了負責展示內容給用戶外還負責響應用戶事件。本章主要介紹UIView用戶交互相關的屬性和方法。

  1、交互相關的屬性

  userInteractionEnabled 默認是YES ,如果設置為NO則不響應用戶事件,並且把當前控件從事件隊列中刪除。也就是說設置了userInterfaceEnabled屬性的視圖會打斷響應者鏈導致該view的subview都無法響應事件。

  multipleTouchEnabled  默認是NO,如果設置為YES則支持多點觸碰。

  exclusiveTouch 默認是NO,如果設置為YES則當前UIView會獨占整個Touch事件。具體來說就是如果UIView設置了exclusiveTouch屬性為YES則當這個UIView成為第一響應者時,在手指離開屏幕前其他view不會響應任何touch事件。

  作用舉例:UITableView的每個cell都需要使用exclusive,否則同時點擊多個cell會觸發每個視圖的事件響應。手勢識別會忽略此屬性。

 

  2、觸摸響應

  了解UIView的觸碰響應之前,首先了解在iOS中觸碰事件是什么,事件在視圖模型中是如何傳遞的,視圖在接收到一個事件是如何響應的。下面介紹觸碰事件類UITouch和響應者鏈來解釋事件的工作原理。

  在iOS中UITouch類代表觸碰事件。當用戶觸摸屏幕后就會產生相應的事件,所有相關的UITouch對象都被包裝在事件中,被程序交由特定的對象處理。UITouch對象包括觸碰的詳細信息。

  UITouch含有5個屬性:

  window:觸碰產生時所處的窗口,由於窗口可能發生變化,當前所在的窗口不一定是最開始的窗口。

  view:觸碰產生時所處的視圖。由於視圖可能發生變化,當前視圖也不一定是最初的視圖。

  tapCount:短時間內輕擊(tap)屏幕的次數,可根據tapCount判斷單擊、雙擊或更多的輕擊。

  timestamp:時間戳記錄了觸碰事件產生或變化時的時間。單位是秒。

  phase:觸碰事件在屏幕上有一個周期,即觸碰開始、觸碰點移動、觸碰結束,中途取消。通過phase可以查看當前觸碰事件在一個周期中所處的狀態。UITouchPhase枚舉:

  UITouchPhaseBegan

  UITouchPhaseMoved

  UITouchPhaseStationary

  UITouchPhaseEnded

  UITouchPhaseCancelled

 

  當手指觸碰到屏幕,無論是單點還是多點觸碰,事件都會開始,直到用戶所有的手指都離開屏幕。期間所有的UITouch對象都被封裝在UIEvent事件對象中,由程序分發給處理者。事件記錄了這個周期中所有觸碰對象狀態的變化。

  只要屏幕被觸摸,系統會將諾干個觸碰信息封裝到UIEvent對象中發送給程序,由管理程序UIApplication對象將事件分發。

 

  響應者對象就是可以響應事件並對事件作出處理的對象。在iOS中UIResponder類定義了響應者對象的所有方法。UIApplication、UIWindow、UIViewController、UIView以及UIKit中繼承自UIView的控件都間接或直接繼承自UIResponder類,這些類都可以當做響應者。

  響應者鏈表示一系列響應者對象組成的事件傳遞的鏈條。當確定了第一響應者后,事件交由第一響應者處理,如果第一響應者不處理事件沿着響應者鏈傳遞,交給下一個響應者。一般來說,第一響應者是UIView對象或者UIView的子類對象,當其被觸摸后事件交由它處理,如果它不處理,事件就會交給它的UIViewController處理(如果存在),然后是它的superview父視圖對象,以此類推,直到頂層視圖。如果頂層視圖不處理則交給UIWindow對象處理,再到UIApplication對象(如果UIApplication繼承自UIResponder)。如果整個響應者鏈都不響應這個事件則該事件被丟棄。

 

  UIView類繼承了UIResponder類,要對事件作出處理還需要重寫UIResponder類中定義的事件處理函數。根據不同的觸碰狀態,程序會調用相應的處理函數,這些函數包括:

  -(void) touchesBegan:(NSSet *)touches withEvents:(UIEvent *)event;

  -(void) touchesMoved:(NSSet *)touches withEvents:(UIEvent *)event;

  -(void) touchesEnded:(NSSet *)touches withEvents:(UIEvent *)event;

  -(void) touchesCancelled:(NSSet *)touches withEvents:(UIEvent *)event;

 

  這幾個方法被調用時,對應了UITouch類中的phase屬性的4個枚舉值。當觸碰被取消,如觸碰過程中被來電打斷,會調用touchesCancelled:touches:方法。

  這些方法在開發中並不需要全部實現,可以根據需要重寫特定的方法。這4個方法都有兩個相同的參數:NSSet類型的touches和UIEvent類型的event。Touches表示觸碰產生的所有的UITouch對象,event表示事件。因為UIEvent包含了整個觸碰過程中所有的觸碰對象,所以可以調用allTouches 方法獲取該事件內所有觸碰對象,也可以調用touchesForView;或者touchesForWindows;取出特定視圖或者窗口上的觸碰對象。在這幾個事件中,都可以拿到觸碰對象,然后根據其位置、狀態、時間屬性做邏輯處理。

 

  輕擊操作很容易引起歧義,比如用戶點擊了一次之后,並不知道用戶是想單擊還是只是雙擊的一部分,或者點了兩次之后並不知道用戶是想雙擊還是繼續點擊。可以使用延遲調用函數解決這個問題。

   

  -(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  {

    UITouch *touch = [touches anyObject];
            if (touch.tapCount == 1)
           {
                   [self performSelector:@selector(setBackground:) withObject:[UIColor blueColor] afterDelay:2];

            }
            else if(touch.tapCount == 2)
            {
                   [self cancelPreviousPerformRequestsWIthTarget:self              selector:@selector(setBackground:) object:[UIColor blueColor]];
                   self.view.backgroundColor = [UIColor redColor];
             }
    }    

  

  除了觸碰事件外UIResponder還提供了運動事件的支持。

  運動事件的方法:

  -(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event 搖動事件開始

  -(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event 搖動事件結束

  -(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event  搖動事件被中斷

 

  遠程事件:

  -(void)remoteControlReceivedWithEvent: 音樂后台播放控制的時候會用到

 

  第一響應者的相關函數:

  - (BOOL)canBecomeFirstResponder    默認返回NO

  - (BOOL)becomeFirstResponder

  - (BOOL)canResignFirstResponder    默認返回YES

  - (BOOL)resignFirstResponder;

  - (BOOL)isFirstResponder

  可以通過becomeFirstResponder方法注冊成為第一響應者,通過resignFirstResponder方法不成為第一響應者。比如通過這兩個方法操作UITextField來控制鍵盤的現隱藏。

 

  3、手勢

  屬性:

  NSArray *gestureRecognizers 

  可以通過這個屬性獲取當前UIView的所有手勢對象。手勢在觸碰事件處理流程中,處於觀察者的角色,其不是view層級結構的一部分,所以不參與響應者鏈。在將觸摸事件發送給hit-test view之前,系統會先將觸碰事件發送到view綁定的Gesture Recognizer上。

  UIView關於手勢的方法:

  -(void) addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer  增加一個手勢。

  -(void) removeGestureRecognizer:(UIGestureRecognizer *)getureRecognizer 刪除一個手勢。

  -(BOOL) gestureRecognizerShouldBegan:(UIGestureRecognizer *)gestureRecognizer 詢問是否開始執行該手勢,默認返回YES。

  手勢相比觸碰事件的好處是可以直接使用已經定義好的手勢,開發者不用自己計算手指移動軌跡。手勢識別的基類是UIGestureRecognizer,是一個抽象類,定義了實現底層手勢識別行為的編程接口。衍生類如下:

  UITabGestureRecognizer         輕擊手勢

  UIPinchGestureRecognizer       捏合手勢

  UIRotationGestureRecognizer    旋轉手勢

  UISwipeGestureRecognizer  輕掃手勢

  UIPanGestureRecognizer 拖拽手勢

  UILongPressGestrueRecognizer 長按手勢

 

  UIGestureRecognizer主要方法:

  -(id) initWithTarget:action: 初始化方法

  -(void)addTarget:action:   

  -(void)removeTarget:action: 

  主要屬性:

  UIGestureRecognizerState state 手勢識別當前狀態

  有以下幾種情況:

  UIGestureRecognizerStatePossibel,  未識別狀態

  UIGestureRecognizerStateBegan,     手勢開始

  UIGestureRecognizerStateChanged,  手勢改變

  UIGestureRecognizerStateEnded, 手勢結束

  UIGestureRecognizerStateFailured 手勢失敗,被其他事件中斷。當把手勢state設為這個值得時候相當於取消了這個手勢。

   cancelsTouchesInView 為YES時,表示當Gesture Recognizers識別到手勢后,會向hit-test view發送touchesCancelled:消息以取消hit-test view對觸碰序列的處理,這樣只有Gesture Recognizer響應此次觸碰,響應者鏈的view不再響應。如果為NO,則不發送touchesCancelled:消息,這樣Gesture Recognizer和view同時響應觸碰事件。默認值是YES。

  delaysTouchesBegan 為NO時表示觸碰序列已經開始而手勢識別還未識別出此手勢時,touch事件會同時發給hit-test view。如果為YES,則手勢在識別過程中,不會有任何觸碰事件發送給hit-test view;如果手勢識別器最終識別了手勢,則也不會發送任何消息給hit-test view;如果手勢識別器最終沒有識別到手勢,才會發送所有觸碰事件給view處理。默認值是NO。

  delaysTouchesBegan 為YES時,延遲發送touchesEnded:消息,手勢失敗時才發送。默認值是YES。

  

  UITabGestureRecognizer  輕擊手勢任意手指任意次數的點擊

  屬性:

  numberOfTapsRequired 點擊次數

  numberOfTouchesRequired 手指個數  

 

  UIPinchGestureRecognizer  捏合或者擴張手勢

  屬性:

  scale:初始值為1,兩手指距離減少則scale不斷變小;兩個手指重合則變為0;

  velocity:初始值為0,手指移動的相對速度,兩手指距離減少為負數,速度越快數值越少;兩手指距離變大為整數,速度越快數值越大。

 

  UIRotationGestureRecognizer 旋轉手勢

  屬性:

  rotation:初始值為0,兩手指的旋轉弧度,順時針旋轉為正數,逆時針旋轉為負數。

  velocity:初始值為0手指一動的相對速度,順時針為正數越快值越大;逆時針為負越快越小。

 

  UISwipGestureRecognizer 輕掃手勢,一個手勢只能指定一個方向,如果需要指定多個方向需要多個手勢

  屬性:

  numberOfTouchesRequired: 手指個數

  direction:手勢方向,如UISwipeGestureRecognizerDirectionRight 向右

 

  UIPanGestureRecognizer:  拖拽手勢,相比輕掃手勢,手指與屏幕的交互時間更長。

  屬性:

  mininumNumberOfTouches 默認值為1,最少手指數量

  maxnumNumberOfTouches 最大手指數量

  方法:

  - (CGPoint)velocityInView:(UIView *)view  返回拖拽手勢的速度,值是每秒移過的point值,被分成水平和垂直兩個分量。

  UILongPressGestrueRecognizer: 長按手勢。

  屬性:

  numberOfTapsRequired:默認值為0,輕擊的次數。

  numberOfTouchesRequired:默認值是1,手指數量。

  mininumPressDuration:默認值為0.5,單位是秒。

  allowableMovement:默認值為10,單位是像素pixels。

 

 

  多手勢兼容

  可以為View添加多個手勢,缺省情況下,沒有對手勢的執行順序排序,每次調用順序可能都不同。通過以下方法可以控制手勢的響應順序。

  - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer

  在作為參數的GestureRecognizer失敗以后手勢才發生,否則手勢從不會發生。

  [self.panRecognizer requireGestureRecognizerToFail:self.swipeRecognizer];  捏合手勢失敗后才會觸發拖拽手勢。如果捏合手勢成功則拖拽手勢永遠不會被觸發

 

  - (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer

  這個方法可以重載,比如UIGestureRecognizer的子類重載了這個方法返回NO,也就是說無論任何情況下子類的手勢都不能被阻止,是非常強勢的手勢。

  如果返回YES,那么preventingGestureRecognizer傳入的手勢就會組織子類手勢。比如:

  [rotationGestureRecognizer canBePreventedByGestureRecognizer:pinchGestureRecognizer]; 如果rotation手勢重載了canBePreventedByGestureRecognizer方法並且返回YES。則旋轉手勢被捏合手勢阻止,但是旋轉手勢不能阻止捏合手勢。

  還可以在方法體中加入邏輯判斷。

 

  - (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer

  這個方法同樣可以重載,如果返回NO則這個手勢不能阻止其他任何手勢。

  如果返回YES,就可以阻止preventedGestureRecognizer的手勢。比如:

  [rotationGestureRecognizer canPreventGestureRecognizer:pinchGestureRecognizer];  如果rotation手勢重載了canBePreventedByGestureRecognizer方法並且返回YES。則旋轉手勢阻止了捏合手勢。

 

  UIGestureRecognizerDelegate

  - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

  此方法在gesture recognizer視圖傳出UIGestureRecognizerStatePossible狀態時調用,如果返回NO,則轉換成UIGestureRecognizerStateFailed;如果返回YES,則繼續識別。默認返回YES

  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

  此方法在window對象有觸碰事件發生時,touchesBegan:withEvent:方法之前調用。如果返回NO,則GestureRecognizer忽略此觸碰事件。默認返回YES。可以用於禁止某個區域的手勢。

   - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
  如果有多個手勢接收到了同一個消息,該回調方法決定當前手勢是否要響應該事件,如果返回YES則該事件被響應,如果返回NO該事件將被忽略

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM