UIGestureRecognizerDelegate學習筆記

原文鏈接

感謝作者ruwatana, 我只是翻譯并學習該文. 如果有錯誤歡迎指正.

前言

如果想控制自定義的UIGestureRecognizer的識別,或者同時控制其他手勢的失敗的情況,我想應該會很多.
這個時候用UIGestureRecognizerDelegate會方便很多.
雖然提供了各種各樣的方法,但是日語的資料不是很多(原文是日語,本文只是翻譯一下),總結了一下哪種情況下會用到哪種方法.

UIGestureRecognizerDelegate

UIGestureRecognizerDelegate是一個為了微調手勢的識別的protocal.
它提供了6個協議方法,都是optional的.

public protocol UIGestureRecognizerDelegate : NSObjectProtocol {
    // 控制Gesture的開始
    @available(iOS 3.2, *)
    optional public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool

    // 控制Gesture是否可以同時識別
    @available(iOS 3.2, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool

    // 控制自身的Gesture和其他Gesture的失敗
    @available(iOS 7.0, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool
    @available(iOS 7.0, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool

    // 控制Gesture是否接受touch
    @available(iOS 3.2, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

    // 控制Gesture是否接受press
    @available(iOS 9.0, *)
    optional public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive press: UIPress) -> Bool
}

使用方法

使用方法很簡單.想要實現協議的對象設置為UIGestureRecognizer的delegate就可以了.

class ViewController: UIViewController {
    var gesture: UIGestureRecognizer?
    override func viewDidLoad() {
        super.viewDidLoad()

        // gesture的初始化

        // 設置delegate
        gesture?.delegate = self
    }
}

extension ViewController: UIGestureRecognizerDelegate {
    // 在這里實現delegate方法
}

delegate方法介紹

在這里將UIGestureRecognizerDelegate分為三類控制方法介紹

  • 識別控制系方法
  • 同時識別控制系方法
  • 失敗控制系方法

識別控制系方法

  • gestureRecognizerShouldBegin 方法

    • 想要控制識別開始與否的時候, 實現該方法. 可能會是UIGestureRecognizerDelegate中所有方法里用途最多的.
    • UIGestureRecognizer的state會根據.possible, .began, .changed, .ended, .cancelled, .failed這種具體的識別狀態發生變化.
    • gestureRecognizerShouldBegin這個方法控制上述狀態中的.possible 是變成.began 還是.failed
    • 如果不實現該方法 default返回值是true.
    • 比如, 想實現控制特定的gesture的場合, 使用如下:
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer === tap0gesture { // 判斷該gesture是否是指定的gesture 從而進行控制
        // hogehoge
        return false  // tap0click不會被調用
    }
    return true  // tap0click會被調用
}
  • shouldReceive touch方法
    • 會先于ShouldBegin之前被調用, 是控制是否接受touch的.如果返回true,則下一步ShouldBegin()方法會被調用.false的場合ShouldBegin()不會被調用
    • 是控制當前view接不接受touch的場合使用的.
      比如:
Screen Shot 2017-10-26 at 9.57.06 AM.png

orangeView的tag是10010

self.view的tag是10012

代碼:

class ViewController: UIViewController {
    
    @objc func tap0click(tap: UITapGestureRecognizer) {
        print("tap 10010 click")
    }
    
    @objc func tap2click(tap: UITapGestureRecognizer) {
        print("tap 10012 click")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 第一個view
        let view0 = CustomView0(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
        self.view.addSubview(view0)
        view0.backgroundColor = UIColor.orange
        view0.tag = 10010
        let tap0 = UITapGestureRecognizer(target: self, action: #selector(ViewController.tap0click(tap:)))
        view0.addGestureRecognizer(tap0)
        tap0.delegate = self
        
        let tap2 = UITapGestureRecognizer(target: self, action: #selector(ViewController.tap2click(tap:)))
        self.view.addGestureRecognizer(tap2)
        self.view.tag = 10012
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

extension ViewController : UIGestureRecognizerDelegate {
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        print("gesture recognizer should receive touch in view : \(String(describing: gestureRecognizer.view?.tag))")
        print("gesture recognizer touch in view \(String(describing: touch.view?.tag))")
        return false
    }
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        print("gesture recognizer should begin in view : \(String(describing: gestureRecognizer.view?.tag))")
        return true
    }
}

返回的結果為:

Screen Shot 2017-10-28 at 10.16.14 PM.png

如果shouldReceive回調方法返回值為true

返回的結果為:

Screen Shot 2017-10-28 at 10.17.06 PM.png
  • shouldReceive press方法和touch類似.
    • 只支持iOS9以上

同時識別控制系方法

  • shouldRecognizeSimultaneouslyWith方法
    • 這個方法是同時識別復數個gesture的時候被使用.
    • 比如viewController的view上有一個scrollView的這種情況, 按理來說,會識別最上面那一層的view, 所以scrollView里內置的gesture會被識別, 而被加到viewController.view的自定義gesture不會被識別.
    • 在上面一種情況下,如果利用shouldRecognizeSimultaneouslyWith這個回調方法, 如果設置同時識別兩種gesture, 加載viewController.view上的自定義的gesture也會被識別.
    • 使用方法很簡單, 想同時識別的兩種gesture, 則返回true, 反之返回false.
    • defalult返回值為false
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    if gestureRecognizer === self.gesture { // 判斷特定手勢
        // hogehoge
    }

    if otherGestureRecognizer is UIPanGestureRecognizer { // 判斷其他另一個手勢
        // hogehoge
    }

    if otherGestureRecognizer.view is UIScrollView { // 判斷其他手勢是加在UIScrollView上的情況
        // hogehoge
    }
    return false
}

還是上面的orageView那個例子.如果設置同時識別回調方法的返回值為false:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return false
}

返回結果:

Screen Shot 2017-10-28 at 8.54.07 PM.png

如果設置返回值為true:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

返回結果:

Screen Shot 2017-10-28 at 8.54.39 PM.png

失敗控制系方法

這個方法是兩個gesture都識別的情況下, 控制gestureRecognizer的失敗, 或者另一個otherGestureRecognizer的失敗的時候使用.

  • shouldRequireFailureOf方法:

    • 這個方法是控制當前gesture本身(設置delegate的gesture)的失敗的方法. 此處很容易與另一個失敗回調shouldBeRequiredToFailBy混淆, 需要注意.
    • 返回true的時候, 控制當前gesture失敗.
    • 在沒有其他gesture的情況下,即使返回true也不會失敗.
    • default值是false
  • shouldBeRequiredToFailBy方法:

    • 與上面的方法對應, 此方法是控制其他方法的成功與失敗.
    • 請注意:如果當前gesture失敗的情況下, 也就是上面的shouldRequireFailureOf方法返回的是true(原文里寫的是false, 感覺寫錯了), 根本不會走到shouldBeRequiredToFailBy方法, 所以這種情況無論返回true或者false都不會生效.

shouldRequireFailureOf方法 返回TRUE時 參數gestureRecognizer會失敗.
shouldBeRequiredToFailBy方法 返回TRUE時 參數otherGestureRecognizer會失敗.

Tips: UIKit中的系統類里使用的gesture是不能設置delegate的.
不是自定義的gesture, 而是UIKit中封裝好的手勢(比如UITableView或者UIScrollView的panGestureRecognizer 等等..)

如果給UITableView(UIScrollView)設置tableview.panGestureRecognizer.delegate = self;, 會崩潰. 提示信息如下:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UIScrollView's built-in pan gesture recognizer must have its scroll view as its delegate.'

原文點擊此處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容