In fact, this would be even better (avoids unnecessary implicitly unwrapped 
optionals):

class ClosureGestureRecognizer<GestureRecognizer: UIGestureRecognizer> {
    fileprivate var recognizer: GestureRecognizer
    private var onAction: ((GestureRecognizer) -> Void)

    init(onAction: @escaping ((GestureRecognizer) -> Void)) {
        self.recognizer = GestureRecognizer()
        self.onAction = onAction

        self.recognizer.addTarget(self, action: #selector(actionHandler))
    }

    @objc private func actionHandler() {
        onAction(recognizer)
    }
}

extension UIView {
    func addGestureRecognizer<T>(_ gestureRecognizer: 
ClosureGestureRecognizer<T>) {
        self.addGestureRecognizer(gestureRecognizer.recognizer)
    }
}

Depending on your use-case, it’d be no problem to make onAction a non-private 
(externally settable) optional closure—in case you want to avoid having to set 
it on init of ClosureGestureRecognizer.

Cheers,
Geordie


> Am 04.06.2017 um 23:09 schrieb Geordie J <geo...@gmail.com>:
> 
> To get around the issue of using self on init, but also that of multiple 
> recogniser types, try this:
> 
> class ClosureGestureRecognizer<RecognizerType: UIGestureRecognizer> {
>     // These are initially nil and set on init to their desired values.
>     // This gets around the issue of using self in init.
>     // `private` means they can't ever actually be nil:
>     private var recognizer: RecognizerType!
>     private var onAction: ((RecognizerType) -> Void)!
> 
>     init(callback: @escaping ((RecognizerType) -> Void)) {
>         recognizer = RecognizerType(target: self, action: 
> #selector(actionHandler))
>         self.onAction = callback
>     }
> 
>     @objc private func actionHandler() {
>         onAction(recognizer)
>     }
> }
> 
> let recognizer = ClosureGestureRecognizer<UIPanGestureRecognizer>(callback: { 
> panGestureRecognizer in
>     print("Panned, translation:", panGestureRecognizer.translation(in: nil))
> })
> 
> Regards,
> Geordie
> 
> 
>> Am 04.06.2017 um 21:55 schrieb Zhao Xin <owe...@gmail.com 
>> <mailto:owe...@gmail.com>>:
>> 
>> Will this work?
>> 
>> class TapGestureRecognizer: UITapGestureRecognizer {
>>     var onTap: (() -> Void)?
>>     init(onTap: (() -> Void)?) {
>>         self.onTap = onTap
>>         super.init(target: nil, action: nil)
>>         self.removeTarget(nil, action: nil)
>>         self.addTarget(self, action: #selector(internalTapHandler))
>>         print(self)
>>     }
>>     
>>     @objc private func internalTapHandler() {
>>         onTap?()
>>     }
>> }
>> 
>> Zhao Xin
>> 
>> On Sun, Jun 4, 2017 at 5:24 PM, Nate Birkholz <nbirkh...@gmail.com 
>> <mailto:nbirkh...@gmail.com>> wrote:
>> Also, note that I tried the following:
>> 
>> class BlockTapGestureRecognizer: UIGestureRecognizer {
>>     var onTap: (() -> Void)?
>>     init(onTap: (() -> Void)?) {
>>         self.onTap = onTap
>>         super.init(target: nil, action: nil)
>>         self.addTarget(self, action: #selector(internalTapHandler))
>>         print(self)
>>     }
>> 
>>     @objc private func internalTapHandler() {
>>         onTap?()
>>     }
>> }
>> 
>> And the object prints looking okay:
>> 
>> <Artmospherez.BlockTapGestureRecognizer: 0x1701998b0; baseClass = 
>> UIGestureRecognizer; state = Possible; view = <UIView 0x100c60870>; target= 
>> <(action=internalTapHandler, target=<Artmospherez.BlockTapGestureRecognizer 
>> 0x1701998b0>)>>
>> 
>> But it doesn't in practice work. The documentation for the (target: action:) 
>> initializer states:
>> 
>> target: An object that is the recipient of action messages sent by the 
>> receiver when it recognizes a gesture. nil is not a valid value.
>> action: A selector that identifies the method implemented by the target to 
>> handle the gesture recognized by the receiver. The action selector must 
>> conform to the signature described in the class overview. NULL is not a 
>> valid value.
>> 
>> So something is going on inside there when the nil values are passed to the 
>> recognizer. As the documentation states, nil is not a valid value and it 
>> must cause troubles.
>> 
>> Or I did something wrong?
>> 
>> 
>> 
>> On Sun, Jun 4, 2017 at 1:55 AM, Nate Birkholz <nbirkh...@gmail.com 
>> <mailto:nbirkh...@gmail.com>> wrote:
>> Ah, I didn't read the rest of the thread. As Zhao Xin notes, you cannot call 
>> super.init(target: self, action: #selector()), and you cannot call 
>> super.init() in a subclass init or convenience init, it *has* to be the 
>> designated init method init(target:action:). That's too bad, closure-based 
>> gesture recognizers would be snazzy and swifty and I'd love to see them as 
>> part of UIKit. 
>> 
>> I could write my own custom implementations of subclassed 
>> UIKit.UIGestureRecognizerSubclass(es), but as the screens in question have 
>> tap, swipe up, swipe down, and swipe right gesture recognizers, I feel its 
>> overkill to write all that to avoid repeating ~10 lines of code.
>> 
>> On Sun, Jun 4, 2017 at 1:21 AM, Nate Birkholz <nbirkh...@gmail.com 
>> <mailto:nbirkh...@gmail.com>> wrote:
>> I briefly considered something like this but didn't explore it. Elegant.
>> 
>> Sent from my iPhone, please excuse brevity and errors
>> 
>> On Jun 3, 2017, at 9:38 PM, Geordie Jay <geo...@gmail.com 
>> <mailto:geo...@gmail.com>> wrote:
>> 
>>> I am dealing with a variant of this on Android right now. I have just 
>>> subclassed e.g. UITapGestureRecognizer to perform the 2nd variant above and 
>>> externally accept a closure as its argument. I'm writing this on my phone 
>>> so forgive any syntax errors or accidental omissions:
>>> 
>>> class TapGestureRecognizer: UITapGestureRecognizer {
>>> var onTap: (() -> Void)?
>>> init(onTap: (() -> Void)?) {
>>> self.onTap = onTap
>>> super.init(target: self, action: #selector(internalTapHandler))
>>> }
>>> 
>>> @objc private func internalTapHandler() {
>>> onTap?()
>>> }
>>> }
>>> 
>>> class Baz: Foo {
>>> init() {
>>> let tapRecognizer = TapGestureRecognizer(onTap: self.bar)
>>> }
>>> }
>>> 
>>> 
>>> Cheers,
>>> Geordie
>>> On Sat 3. Jun 2017 at 16:53, Nate Birkholz via swift-users 
>>> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
>>> Thanks, the second had occurred to me, but felt a little too much like in 
>>> practice it would make the code harder to understand.
>>> 
>>> On Fri, Jun 2, 2017 at 9:58 PM, Zhao Xin <owe...@gmail.com 
>>> <mailto:owe...@gmail.com>> wrote:
>>> I found two workarounds.
>>> 
>>> 1.
>>> protocol Foo: class {
>>>     func bar()
>>> }
>>> 
>>> class Base:Foo {
>>>     @objc func bar() {
>>>         print("bar")
>>>     }
>>> }
>>> 
>>> class Baz: Base {
>>>     override init() {
>>>         super.init()
>>>         let tapRecognizer = UITapGestureRecognizer(target: self, action: 
>>> #selector(bar))
>>>     }
>>> }
>>> 
>>> 2.
>>> protocol Foo: class {
>>>     func bar()
>>> }
>>> 
>>> extension Foo {
>>>     func bar() {
>>>         print("bar")
>>>     }
>>> }
>>> 
>>> class Baz: Foo {
>>>     init() {
>>>         let tapRecognizer = UITapGestureRecognizer(target: self, action: 
>>> #selector(delegate))
>>>     }
>>>     
>>>     @objc func delegate() {
>>>         bar()
>>>     }
>>> }
>>> 
>>> 
>>> Zhao Xin
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> On Sat, Jun 3, 2017 at 10:35 AM, Nate Birkholz via swift-users 
>>> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
>>> protocol Foo: class {
>>>     func bar()
>>> }
>>> 
>>> extension Foo {
>>>     func bar() {
>>>          print("bar")
>>>     }
>>> }
>>> 
>>> class Baz: Foo {
>>>     init() {
>>>         let tapRecognizer = UITapGestureRecognizer(target: self, action: 
>>> #selector(bar))
>>>     }
>>> }
>>> 
>>> the #selector tells me: "Argument of '#selector' refers to instance method 
>>> 'bar()' that is not exposed to Objective-C" and asks me to add @objc to the 
>>> method definition. 
>>> 
>>> Adding @objc to the method tells me: "@objc can only be used with members 
>>> of classes, @objc protocols, and concrete extensions of classes"
>>> 
>>> Adding @objc to the protocol doesn't fix it, just introduces new issues.
>>> 
>>> "dynamic" cannot be applied to a protocol, so cannot be used alternatively. 
>>> 
>>> Is there a way to get around this? If a method is called by a gesture 
>>> recognizer, is there no way to have a default protocol implementation? I'd 
>>> like to use default implementations if possible to make my code more DRY.
>>> 
>>> Is there a roadmap/plan for swift-native selector dispatch?
>>> 
>>> Thanks. I look forward to the inevitable reply revealing the dumb thing I 
>>> missed. :)
>>> 
>>> -- 
>>> Nate Birkholz
>>> 
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users@swift.org <mailto:swift-users@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-users 
>>> <https://lists.swift.org/mailman/listinfo/swift-users>
>>> 
>>> 
>>> 
>>> 
>>> 
>>> -- 
>>> Nate Birkholz
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users@swift.org <mailto:swift-users@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-users 
>>> <https://lists.swift.org/mailman/listinfo/swift-users>
>> 
>> 
>> 
>> -- 
>> Nate Birkholz
>> 
>> 
>> 
>> -- 
>> Nate Birkholz
>> 
> 

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to