Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIGestureRecognizer with closure

I have as view, in which I want to perform a swipe right gesture. Unfortunately I receive the error EXC_BAD_ACCESS. Does anybody know what's wrong here ? Please a look at the code below.

extension UIView {

    func addGestureRecognizerWithAction(nizer: UIGestureRecognizer, action:() -> ()) {

        class Invoker {
            var action:() -> ()
            init(action:() -> ()) {
                self.action = action
            }
            func invokeTarget(nizer: UIGestureRecognizer) {
                self.action()
                println("Hi from invoker")
            }
        }
        addGestureRecognizer(nizer)
        nizer.addTarget(Invoker(action), action: "invokeTarget:")
    }
}

class BugView: UIView {

    override func awakeFromNib() {
        super.awakeFromNib()

        var swipeRight = UISwipeGestureRecognizer()
        swipeRight.direction = UISwipeGestureRecognizerDirection.Right
        self.addGestureRecognizerWithAction(swipeRight) {
            println("Hi from the gesture closure")
        }
    }
}
like image 501
user1755189 Avatar asked Oct 06 '14 20:10

user1755189


2 Answers

For those who does not like associated objects and other tough stuff I have a small gesture recogniser that does not need any hustle. And it doesn't need to be stored somewhere. Works like a regular GR. Though it has some limitations - it's inherited from UITapGestureRecognizer and he knows only how to handle taps. But do we often need all the types there are? Personally me not.

final class BindableGestureRecognizer: UITapGestureRecognizer {
    private var action: () -> Void

    init(action: @escaping () -> Void) {
        self.action = action
        super.init(target: nil, action: nil)
        self.addTarget(self, action: #selector(execute))
    }

    @objc private func execute() {
        action()
    }
}

Usage example:

    let gestureRecognizer = BindableGestureRecognizer {
        print("It's alive!")
    }
    self.view.addGestureRecognizer(gestureRecognizer)

In case some more meaningful lines are presented, don't forget about [weak self]. We don't want to create those undead buddies.

    let colorGestureRecognizer = BindableGestureRecognizer { [weak self] in
        self?.view.backgroundColor = .red
    }
    self.view.addGestureRecognizer(colorGestureRecognizer)

Seems handy for me to purify our view controller from those @objc one liners and to become a bit more.. reactive?

like image 129
Pavel Stepanov Avatar answered Oct 24 '22 20:10

Pavel Stepanov


Finally return true was right. In his answer above he pointed me in the right direction, by suggesting to make Invoker a subclass of NSObject.

But this was not the only mistake I had in my code. I figured out that when the swipe occurred, the Invoker object, which was meant to handle the event, had already disappeared from memory. I think the reference to it was not captured by the closure as it should have been. Now I figured out another way of implementing this feature which I would like to share with you:

class UIGestureRecognizerWithClosure: NSObject {  // must subclass NSObject otherwise error: "class does not implement methodSignatureForSelector: -- " !!

    var closure:() -> ()

    init(view:UIView, nizer: UIGestureRecognizer, closure:() -> ()) {
        self.closure = closure
        super.init()
        view.addGestureRecognizer(nizer)
        nizer.addTarget(self, action: "invokeTarget:")
    }

    func invokeTarget(nizer: UIGestureRecognizer) {
        self.closure()
    }
}

And this snippet shows how you can use the code:

var swipeRight = UISwipeGestureRecognizer()
swipeRight.direction = UISwipeGestureRecognizerDirection.Right

// swipeWrapper has to be defined as a property: var swipeWrapper:UIGestureRecognizerWithClosure?
// -> this is needed in order to keep the object alive till the swipe event occurs
swipeWrapper = UIGestureRecognizerWithClosure(view: self, nizer:swipeRight) {
    println("Hi from the gesture closure")
}
like image 32
user1755189 Avatar answered Oct 24 '22 21:10

user1755189