Here’s what I’d try.

(disclaimer: written in Mail, may contain errors, yadda yadda yadda)

public class BlockTapGestureRecognizer: UITapGestureRecognizer {
    private class Target: NSObject {
        private let closure: (UITapGestureRecognizer) -> ()
        
        init(closure: @escaping (UITapGestureRecognizer) -> ()) {
            self.closure = closure
            super.init()
        }
        
        @objc func performClosure(_ sender: Any?) {
            guard let recognizer = sender as? UITapGestureRecognizer else {
                print("Unexpected sender (expected UITapGestureRecognizer)")
                return
            }
            
            self.closure(recognizer)
        }
    }
    
    private let target: Target
    
    public init(closure: @escaping (UITapGestureRecognizer) -> ()) {
        self.target = Target(closure: closure)
        super.init(target: self.target, action: 
#selector(Target.performClosure(_:)))
    }
}

No need for weird hacks passing nil as the target or action.

Charles

> On Jun 4, 2017, at 11:08 AM, Nate Birkholz via swift-users 
> <swift-users@swift.org> wrote:
> 
> Technically it does work(!), but I am hesitant to use it in what amounts to 
> sample code for my current job search, haha.
> 
> On Sun, Jun 4, 2017 at 4:55 AM, Zhao Xin <owe...@gmail.com> wrote:
> 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> 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> 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> 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> 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> 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> 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> 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
>> https://lists.swift.org/mailman/listinfo/swift-users
>> 
>> 
>> 
>> 
>> 
>> -- 
>> Nate Birkholz
>> _______________________________________________
>> swift-users mailing list
>> swift-users@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
> 
> 
> 
> -- 
> Nate Birkholz
> 
> 
> 
> -- 
> Nate Birkholz
> 
> 
> 
> 
> -- 
> Nate Birkholz
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

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

Reply via email to