Gesture recognizer not working if created at class

2019-04-15 10:49发布

问题:

In a collection view, I create a gesture recognizer at class init time. In the viewDidLoad method, I then add the gesture recognizer to the collection view.

class ViewController: UIViewController {
    @IBOutlet weak var collectionView: UICollectionView!
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture(gesture:)))

    @objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
        // some code
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.addGestureRecognizer(longPressGesture)
    }
}

With this, the gesture recognizer does not work.

The fix is easy: it suffices to move the line with let longPressGesture to the viewDidLoad method and everything works as expected. However, I find it a bit surprising that the first version would not work.

Can anyone explain why the first version is not working? Is it because, when the gesture recognizer is created, the collection view is not yet ready to have gestures? So, what must a gesture recognizer know about its target in order to be created?

回答1:

Good question. That's because you are trying to use self when not fully initialized.

Now, how to make that work with the way you wanted? Perhaps declare it lazily, like so:

private lazy var longPressGesture: UILongPressGestureRecognizer! = {
    let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongGesture(gesture:)))
    return gesture
}()

Edit: Quoting giorashc's answer from this question:

Due to swift's 2-phase initialization you need to initialize the parent class before you can use self in the inheriting class.

In your implementation self is yet to be initialized by the parent class so as you said you should move it to the init method of your view controller and create the button after calling the parent's initialization method

2 Phase Initialization SO Question & Answer.