感謝作者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的場合使用的.
比如:
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
}
}
返回的結果為:
如果shouldReceive回調方法返回值為true
返回的結果為:
- 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
}
返回結果:
如果設置返回值為true:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
返回結果:
失敗控制系方法
這個方法是兩個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.'