感謝作者ruwatana, 我只是翻譯并學(xué)習(xí)該文. 如果有錯(cuò)誤歡迎指正.
前言
如果想控制自定義的UIGestureRecognizer的識(shí)別,或者同時(shí)控制其他手勢(shì)的失敗的情況,我想應(yīng)該會(huì)很多.
這個(gè)時(shí)候用UIGestureRecognizerDelegate會(huì)方便很多.
雖然提供了各種各樣的方法,但是日語(yǔ)的資料不是很多(原文是日語(yǔ),本文只是翻譯一下),總結(jié)了一下哪種情況下會(huì)用到哪種方法.
UIGestureRecognizerDelegate
UIGestureRecognizerDelegate是一個(gè)為了微調(diào)手勢(shì)的識(shí)別的protocal.
它提供了6個(gè)協(xié)議方法,都是optional的.
public protocol UIGestureRecognizerDelegate : NSObjectProtocol {
// 控制Gesture的開(kāi)始
@available(iOS 3.2, *)
optional public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool
// 控制Gesture是否可以同時(shí)識(shí)別
@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
}
使用方法
使用方法很簡(jiǎn)單.想要實(shí)現(xiàn)協(xié)議的對(duì)象設(shè)置為UIGestureRecognizer的delegate就可以了.
class ViewController: UIViewController {
var gesture: UIGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
// gesture的初始化
// 設(shè)置delegate
gesture?.delegate = self
}
}
extension ViewController: UIGestureRecognizerDelegate {
// 在這里實(shí)現(xiàn)delegate方法
}
delegate方法介紹
在這里將UIGestureRecognizerDelegate分為三類(lèi)控制方法介紹
- 識(shí)別控制系方法
- 同時(shí)識(shí)別控制系方法
- 失敗控制系方法
識(shí)別控制系方法
-
gestureRecognizerShouldBegin 方法
- 想要控制識(shí)別開(kāi)始與否的時(shí)候, 實(shí)現(xiàn)該方法. 可能會(huì)是UIGestureRecognizerDelegate中所有方法里用途最多的.
- UIGestureRecognizer的state會(huì)根據(jù).possible, .began, .changed, .ended, .cancelled, .failed這種具體的識(shí)別狀態(tài)發(fā)生變化.
- gestureRecognizerShouldBegin這個(gè)方法控制上述狀態(tài)中的.possible 是變成.began 還是.failed
- 如果不實(shí)現(xiàn)該方法 default返回值是true.
- 比如, 想實(shí)現(xiàn)控制特定的gesture的場(chǎng)合, 使用如下:
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer === tap0gesture { // 判斷該gesture是否是指定的gesture 從而進(jìn)行控制
// hogehoge
return false // tap0click不會(huì)被調(diào)用
}
return true // tap0click會(huì)被調(diào)用
}
- shouldReceive touch方法
- 會(huì)先于ShouldBegin之前被調(diào)用, 是控制是否接受touch的.如果返回true,則下一步ShouldBegin()方法會(huì)被調(diào)用.false的場(chǎng)合ShouldBegin()不會(huì)被調(diào)用
- 是控制當(dāng)前view接不接受touch的場(chǎng)合使用的.
比如:

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()
// 第一個(gè)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
}
}
返回的結(jié)果為:

如果shouldReceive回調(diào)方法返回值為true
返回的結(jié)果為:

- shouldReceive press方法和touch類(lèi)似.
- 只支持iOS9以上
同時(shí)識(shí)別控制系方法
- shouldRecognizeSimultaneouslyWith方法
- 這個(gè)方法是同時(shí)識(shí)別復(fù)數(shù)個(gè)gesture的時(shí)候被使用.
- 比如viewController的view上有一個(gè)scrollView的這種情況, 按理來(lái)說(shuō),會(huì)識(shí)別最上面那一層的view, 所以scrollView里內(nèi)置的gesture會(huì)被識(shí)別, 而被加到viewController.view的自定義gesture不會(huì)被識(shí)別.
- 在上面一種情況下,如果利用shouldRecognizeSimultaneouslyWith這個(gè)回調(diào)方法, 如果設(shè)置同時(shí)識(shí)別兩種gesture, 加載viewController.view上的自定義的gesture也會(huì)被識(shí)別.
- 使用方法很簡(jiǎn)單, 想同時(shí)識(shí)別的兩種gesture, 則返回true, 反之返回false.
- defalult返回值為false
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer === self.gesture { // 判斷特定手勢(shì)
// hogehoge
}
if otherGestureRecognizer is UIPanGestureRecognizer { // 判斷其他另一個(gè)手勢(shì)
// hogehoge
}
if otherGestureRecognizer.view is UIScrollView { // 判斷其他手勢(shì)是加在UIScrollView上的情況
// hogehoge
}
return false
}
還是上面的orageView那個(gè)例子.如果設(shè)置同時(shí)識(shí)別回調(diào)方法的返回值為false:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
返回結(jié)果:

如果設(shè)置返回值為true:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
返回結(jié)果:

失敗控制系方法
這個(gè)方法是兩個(gè)gesture都識(shí)別的情況下, 控制gestureRecognizer的失敗, 或者另一個(gè)otherGestureRecognizer的失敗的時(shí)候使用.
-
shouldRequireFailureOf方法:
- 這個(gè)方法是控制當(dāng)前gesture本身(設(shè)置delegate的gesture)的失敗的方法. 此處很容易與另一個(gè)失敗回調(diào)shouldBeRequiredToFailBy混淆, 需要注意.
- 返回true的時(shí)候, 控制當(dāng)前gesture失敗.
- 在沒(méi)有其他gesture的情況下,即使返回true也不會(huì)失敗.
- default值是false
-
shouldBeRequiredToFailBy方法:
- 與上面的方法對(duì)應(yīng), 此方法是控制其他方法的成功與失敗.
- 請(qǐng)注意:如果當(dāng)前gesture失敗的情況下, 也就是上面的shouldRequireFailureOf方法返回的是true(原文里寫(xiě)的是false, 感覺(jué)寫(xiě)錯(cuò)了), 根本不會(huì)走到shouldBeRequiredToFailBy方法, 所以這種情況無(wú)論返回true或者false都不會(huì)生效.
shouldRequireFailureOf方法 返回TRUE時(shí) 參數(shù)gestureRecognizer會(huì)失敗.
shouldBeRequiredToFailBy方法 返回TRUE時(shí) 參數(shù)otherGestureRecognizer會(huì)失敗.
Tips: UIKit中的系統(tǒng)類(lèi)里使用的gesture是不能設(shè)置delegate的.
不是自定義的gesture, 而是UIKit中封裝好的手勢(shì)(比如UITableView或者UIScrollView的panGestureRecognizer 等等..)
如果給UITableView(UIScrollView)設(shè)置tableview.panGestureRecognizer.delegate = self;, 會(huì)崩潰. 提示信息如下:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UIScrollView's built-in pan gesture recognizer must have its scroll view as its delegate.'