adlere
(Ehud Adler)
1
I've been looking into UIGestureRecognizer trying to allow something like this:
let tap = UITapGestureRecognizer { (tapGest) in
// Code that will be run when the gesture fires
}
Just a quick way of creating a gesture and its target during init.
I can get everything working with:
extension UIGestureRecognizer {
convenience init(_ action: @escaping (UIGestureRecognizer) -> ()) {
self.init()
action(self)
}
}
but this requires me to call
let tap = UITapGestureRecognizer { (tapGest: UITapGestureRecognizer) in
I've tried
convenience init<T>(_ action: @escaping (T) -> ()) {
but i get ( _ ) unless I :UITapGestureRecognizer
It's not a big deal to just tell the compiler the type is UITapGesture.. but since I already decalred
let tap = UITapGestureRecognizer( I thought maybe the constructor would know the proper type.
I'm curious as to why the compiler doesn't know the type in the
convenience init<T>(_ action: @escaping (T) -> ()) {
case and if theres a way to get this to work.
let tap = UITapGestureRecognizer { (tapGest) in
// Code that will be run when the gesture fires
}
Note: I already got some code working that can take the closure and store it as the gestures target so I'm not curious about that aspect.
Thanks!
1 Like
mayoff
(Rob Mayoff)
2
What you really want to say is something like
convenience init(_ action: @escaping (Self) -> ()) { ... }
but that's not allowed because you can only use Self in a protocol. So use a protocol to define the init method, and then conform all of the gesture recognizer classes to the protocol:
import UIKit
import ObjectiveC
protocol GestureRecognizerActionBlockSupport where Self: UIGestureRecognizer { }
extension GestureRecognizerActionBlockSupport {
init(_ actionBlock: @escaping (Self) -> ()) {
self.init(target: nil, action: nil)
self.actionBlock = { [weak self] in
if let me = self {
actionBlock(me)
}
}
addTarget(self, action: #selector(callActionBlock))
}
}
fileprivate extension UIGestureRecognizer {
@IBAction func callActionBlock() {
actionBlock?()
}
var actionBlock: (() -> ())? {
get {
return objc_getAssociatedObject(self, &actionBlockKey) as? (() -> ())
}
set {
objc_setAssociatedObject(self, &actionBlockKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
private var actionBlockKey = 0
extension UITapGestureRecognizer: GestureRecognizerActionBlockSupport { }
extension UIPanGestureRecognizer: GestureRecognizerActionBlockSupport { }
extension UILongPressGestureRecognizer: GestureRecognizerActionBlockSupport { }
extension UIPinchGestureRecognizer: GestureRecognizerActionBlockSupport { }
extension UIRotationGestureRecognizer: GestureRecognizerActionBlockSupport { }
extension UISwipeGestureRecognizer: GestureRecognizerActionBlockSupport { }
2 Likes
adlere
(Ehud Adler)
3
Ya! That did it. Any chance you could explain a bit more whats going on so I fully understand.
mayoff
(Rob Mayoff)
4
Which parts are you having trouble understanding?
adlere
(Ehud Adler)
5
So I guess first off I'm curious as to why th eneed to import ObjectiveC is this just to finish up the whole example with the best way to approach adding the actionBlock?
- Also where does the restriction to access except in a protocol come from? Is this a design choice in swift?
Otherwise I think it makes sense. Really cool stuff.
mayoff
(Rob Mayoff)
6
I imported ObjectiveC to use objc_getAssociatedObject and objc_setAssociatedObject so that I could provide a complete, working implementation.
As for the restriction (on use of Self, I assume), I assume it's not allowed because it has lots of weird cases the compiler would have to handle. Suppose you write this:
class Base {
var callback: ((Self) -> ())? = nil
var otherBase: Base?
func doSomethingWeird() {
if let callback = callback, let otherBase = otherBase {
callback(otherBase)
}
}
}
class Derived {
// inherits Base.callback
}
What should Self mean here? Probably you want callback to be a (Base) -> () on an instance of Base, and a (Derived) -> () on an instance of Callback. But then the call to callback in doSomethingWeird function is invalid if you call doSomethingWeird on a Derived. There are probably lots of subtleties like this that would need to be specified and then correctly implemented in the compiler.
1 Like
adlere
(Ehud Adler)
7
Ah okay, I think I get that. One more question. Why did each gesture individually need to confrom to the Protocol? Why couldnt just UIGestureRecognizer conform?
mayoff
(Rob Mayoff)
8
Well, I thought if I conformed UIGestureRecognizer to the protocol, then the init would always have this signature, even in subclasses:
init(_ actionBlock: @escaping (UIGestureRecognizer) -> ())
That is, I didn't think init would take a closure that knew about the specific subclass you were initializing.
However, now I've tested it and found out I was wrong. You can just conform UIGestureRecognizer to the protocol and not conform the individual subclasses. The init will be specialized for each subclass.
1 Like
adlere
(Ehud Adler)
9
Thanks so much. Great answer