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