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")
}
}
}
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?
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")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With