en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

12.06.2012

手勢辨識 UIGestureRecognizer 物件

  

手勢辨識,是 iOS Device 不可或缺的互動條件之一,辨識的方法除了使用 UITouch 來做觸碰點的擷取外,你也可以例用 UIGestureRecognizer 物件來幫助你做簡單的手勢辨識,UIGestureRecognizer 已經替使用者定義的一些現成的手勢,像是滑動、掐、點擊、旋轉、拖曳或是按住不放等,下面就示範了如何透過 UIGestureRecognizer 物件,使用程式碼和 Storyboard 介面設計的方式來做手勢的辨識。


UIGestureRecognizer 提供的辨識種類
UIGestureRecognizer 目前提供 6 種不同的手勢辨識,你可以在元件庫中找到由 UIGestureRecognizer 所衍生出來的這些元件。

//點擊動作手勢辨識
UITapGestureRecognizer;

//掐擠動作的手勢辨識
UIPinchGestureRecognizer;

//旋轉動作的手勢辨識
UIRotationGestureRecognizer;

//滑動動作的手勢辨識
UISwipeGestureRecognizer;

//拖曳動作的手勢辨識
UIPanGestureRecognizer;

//久按動作的手勢辨識
UILongPressGestureRecognizer;

由 UIGestureRecognizer 所衍生出的 6 種不同的手勢辨識元件


使用 Storyboard 或是 xib 檔做設定
使用介面設計的方式來實作 UIGestureRecognizer 的手勢辨識,有下幾個步驟:

  • 將欲使用的手勢辨識元件拖曳至想要互動的 UIView 上。
在拖曳完成之後,回到你的手勢辨識元件上,檢視 Connections Inspector 中的 Referencing Outlet Collections ,確認有與想要互動的 UIView 做好連結。
將手勢辨識元件與欲互動的 UIView 做好連結


  • 設定手勢辨識元件的行為
手勢辨識元件決定了與 UIView 的互動方式,但是在相同的互動方式下,還有著更多的細項可以做設定,在此,我們暫且稱這些設定為「行為」,例如 UISwipeGestureRecognizer 互動元件有著曹向不同方向滑動的行為,或是使用不同數量的手指做滑動的行為,而這些行為都可以從該元件的 Attributes Inspector 中做詳細的設定。
在 UISwipeGestureRecognizer 元件中設定以 3 之手指做向下滑動的行為


  • 設定成功辨識手勢之後所要觸發的方法函式
在手勢辨識成功之後,還必須設定一個方法函式來響應這個事件,設定的方式非常簡單,就如同設定按鈕事件般,將事件與對應的方法函式做拖曳的連結即可即可(對 ViewController 做連結,並非First Responder)。
設定成功辨識手勢時所響應的方法函式
- (IBAction)swipeDownByThreeFingers:(id)sender {
    [gestureLabel setText:@"三隻手指向下滑動"];
}


  • 補充說明
當完成上述步驟後,在你的 Storyboard 或是 xib 的物件檢視器中,應該會看到類似下面的布局。
UISwipeGestureRecognizer 物件的 Connections Inspector

欲互動區域 UIView 物件的 Connections Inspector

在欲互區域 UIView 物件的 Connections Inspector 裡,可以看到在 Outlet Collections 部分有一個白色的警示驚嘆號,其原因是當我們在拖曳 UIGestureRecognizer 至互動區域做連結設定時,並未在該 UIView 的 Class 中宣告一個 gestureRecognizers 物件來做連結,導致在 Connections Inspector 會出現找不到名稱為 gestureRecognizers 物件的警告,不過這並不會影響執行的結果。


使用程式碼做設定
//宣告一個點擊手勢辨識的物件
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapThreeTimesByOneFinger)];

//設定行為
[tapGestureRecognizer setNumberOfTouchesRequired:1];
[tapGestureRecognizer setNumberOfTapsRequired:3];

//加入欲互動區的 UIView 中
[recognizerView addGestureRecognizer:tapGestureRecognizer];

在上述程式碼中,我們建立了一個點擊手勢辨識的物件,並且設定在成功辨識時必須觸發相同類別中的 tapThreeTimesByOneFinger 方法函式;在行為方面,設定為單一手指連續點擊三下,最後,將此手勢辨識的物件 tapGestureRecognizer 加入到欲互動的 UIView 中。


ps:如果是針對滑動的手勢辨識,你可以參考簡單判斷 Swipe 手勢劃過方向的方法一文,在同一個方法函式中,使用 UISwipeGestureRecognizerDirection 來判斷不同方向的滑動手勢。


判斷 UIGestureRecognizer 的狀態
這是一個比較進階的用法,當你想要自訂一些比較特殊且複雜的手勢時,可以參考使用 UIGestureRecognizer 的 state 來幫助判斷,在 UIGestureRecognizer.h 中,可以發現 UIGestureRecognizer 包含以下幾種狀態。
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
    UIGestureRecognizerStatePossible, // the recognizer has not yet recognized its gesture, but may be evaluating touch events. this is the default state

    UIGestureRecognizerStateBegan, // the recognizer has received touches recognized as the gesture. the action method will be called at the next turn of the run loop
    UIGestureRecognizerStateChanged, // the recognizer has received touches recognized as a change to the gesture. the action method will be called at the next turn of the run loop
    UIGestureRecognizerStateEnded, // the recognizer has received touches recognized as the end of the gesture. the action method will be called at the next turn of the run loop and the recognizer will be reset to UIGestureRecognizerStatePossible
    UIGestureRecognizerStateCancelled, // the recognizer has received touches resulting in the cancellation of the gesture. the action method will be called at the next turn of the run loop. the recognizer will be reset to UIGestureRecognizerStatePossible

    UIGestureRecognizerStateFailed, // the recognizer has received a touch sequence that can not be recognized as the gesture. the action method will not be called and the recognizer will be reset to UIGestureRecognizerStatePossible

    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded // the recognizer has received touches recognized as the gesture. the action method will be called at the next turn of the run loop and the recognizer will be reset to UIGestureRecognizerStatePossible
};

UIGestureRecognizer 的狀態並不會是單一存在的,而是有一種既定的順序,因此在設計自訂的手勢時,必須要能很清楚的分辨每個階段,如下圖。

一些可能的 UIGestureRecognizer 狀態改變(圖片來源自 iOS Developer Library


大多數的手勢辨識都是由 UIGestureRecognizerStatePossible 做開始,在 UIGestureRecognizerStateRecognized 或是 UIGestureRecognizerStateFailed 做結束,表示辨識成功與失敗,你可以在 iOS DeveloperLibrary上的 Gesture Recognizers, Creating Custom Gesture Recognizers 章節內,找到更多的應用。






13 則留言:

  1. 你好,
    我一直都有在追蹤你們發佈的文章.
    不管是third party class或着是教學文章都讓我有不少收獲,
    我覺得美中不足的一點就是,
    因為文章也有寫出關鍵的程式碼了,
    為何不連同整個sample code一起放出來呢?

    回覆刪除
  2. Tom 與 匿名者 您好:

    很高興我們的教學網站對您有所幫助,目前暫時沒有提供開放下載的考量,如果您需要完整程式碼加以琢磨,可以來信索取。

    至於沒有將完整程式碼放上來的原因,多半是因為版面的問題,眾多複雜的程式碼會使得讀者難以閱讀,而每個人寫程式的習慣,也不盡相同,如果將完整程式碼全數放上,勢必還需要解釋其耦合關係,這樣就失去了該篇文章的焦點,這也違背了我們當初設立網站的初衷,這點還請您見諒。

    回覆刪除
  3. Wayne Liu6/04/2012

    請問文章中提到的第一張小圖(標題為「將手勢辨識元件與欲互動的 UIView 做好連結」),請問這張圖是您在gesturerecognizer的connection inspector看到的,還是在uiview的connection inspector看到的?

    在我的專案裡面(甚至是apple官方的專案),若是去觀看"gesturerecognizer"的connection inspector,會看到類似於您所貼的圖,但如果去觀看uiview的connection inspector,則會發現Outlet Connections裡面的項目右上角會有個驚嘆號。

    爬了文也找不到它的問題出在哪裡,但是整個專案卻又可以正常執行,不知道板主是否也有發現這個問題?

    回覆刪除
    回覆
    1. Wayne Liu 您好:

      剛剛已經將文章內容做更新,非常抱歉造成您的誤解。

      關於你說的那一張圖片,它是在 UIGestureRecognizer 物件中的 Connections Inspector 所看到的內容。

      另外,您所說的問題,我不太確定是不是 Xcode 的 bug,因為當我們在 Storyboard 或是 xib 上,使用物件對物件做拖曳設定時,會自動對兩個物件建立「雙向」的 Association,才會導致這樣的問題產生。

      刪除
    2. Wayne Liu6/05/2012

      謝謝您的答覆,請不必擔心,我並沒有誤解您文章的意義,只是不知道那個圖是從哪個物件的角度檢視,所以問問以便我帶出這個驚嘆號的問題~

      關於那個驚嘆號,我的猜測也和您一樣覺得是Xcode的bug。這麼說來您應該也同意可以直接忽略那個驚嘆號囉。

      刪除
    3. 是的,我也是直接忽略它,在執行上也沒有發現何問題。

      昨天我也試著寫一個 UIView 的 subclass 並且宣告一個具有 IBOutlet 識別標籤的 gesturerecognizers 物件變數在其中,同樣也是會有這個問題產生。

      上網找了一下資料,大家好像都有相同的疑問,只是目前都是無解的狀況。如果將來有找到相關的解決辦法,我會在寫一篇文章加以說明的。

      刪除
  4. 你好!

    我想建立一幅可以放大縮小,拖曳移動及可旋轉的圖片,
    想法是把rotation gesture 加進UIScrollView中,
    可就是無法實行。
    想問如果要在UIScrollView 中使用gesturerecognizers 物件要注意什麼呢?
    在網上搜尋過,說是因為UIScrollView 它本身已有自身的 gesturerecognizers,
    所以後加的gesturerecognizers 會被忽略。
    想問有什麼辦法可以建立一幅可以放大縮小,拖曳移動及可旋轉的圖片?

    回覆刪除
    回覆
    1. Jacky 您好:

      我不太確定您是因為什麼原因的考量之下決定使用 UIScrollView 當做手勢辨識的平台介面,如果您只是要做出縮放、移動與旋轉的功能,直接使用UIImageView就可以做到了。

      另外如果非得使用UIScrollView 那麼可以考慮看看,在UIScrollView的外面在加上一層 UIView,並且對這一層 UIView 做手勢辨識。

      我自己試了一下UIScrollView與 gesturerecognizers,並沒有您所說的這些問題,旋轉、拖曳、滑動與放大縮小都可以正常取得,我用的是iOS SDK 6.0。
      提醒您幾個地方
      - 欲做辨識的UIView 記得要將 User Interaction Enabled、Multiple Touch 打開。
      - 如果是使用介面編輯器,在連結辨識成功所要觸發的方法函式時,連結的位置應該是「UIView Controller」並非「First Responder]。
      -如果想要取得細部數值,可以在觸發函式的傳回參數設定gesturerecognizers的類型,例如旋轉:
      - (IBAction)rotation:(UIRotationGestureRecognizer *)sender {
      NSLog(@"%f",sender.rotation);

      希望對你有幫助!

      刪除
    2. 很感謝你的答覆!
      「在UIScrollView的外面在加上一層 UIView」
      一直沒想到這方法,真的非常有用!感謝 !

      感謝你們提供那麼多不同題材的教學!

      但亦明白program 的題材實在多不勝數,

      如果日後有問題要請教,但在貴教學網找不到相關教學時,

      請問應該在那發問?

      刪除
    3. Jack 您好:

      您太客氣了,寫是程式需要互相討論才會進步的,我們也不是什麼都會^^"

      只是目前並沒有建立論壇或是討論區的打算,因為坊間已經有許多類似性質的網站,
      但是你仍然可以到我們的 Facebook 的粉絲團,或是寄信來留下您的意見一同討論。

      https://www.facebook.com/pages/Furnace-iOS/208742699144374
      或是從網頁右上角的粉絲團圖示進入

      感謝您的支持!

      刪除
  5. 進入method後,要接續判斷狀態如:
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan)

    感謝這篇教學!

    回覆刪除
    回覆
    1. cg2010studio 您好:

      您說的沒錯,這的確是比較正確且保險的作法,謝謝。

      刪除