[swift-users] Calling default implementation of protocol methods as selectors

Nate Birkholz nbirkholz at gmail.com
Sun Jun 4 12:50:25 CDT 2017


That works great and is very elegant. Nice work.

On Sun, Jun 4, 2017 at 6:34 AM, Geordie J <geojay at gmail.com> wrote:

> 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 <geojay at 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 <owenzx at 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 <nbirkholz at 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 <nbirkholz at 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 <nbirkholz at 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 <geojay at 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 at 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 <owenzx at 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 at 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 at swift.org
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Nate Birkholz
>>>>> _______________________________________________
>>>>> swift-users mailing list
>>>>> swift-users at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>>
>>>>
>>>
>>>
>>> --
>>> Nate Birkholz
>>>
>>
>>
>>
>> --
>> Nate Birkholz
>>
>
>
>
>


-- 
Nate Birkholz
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170604/0efb2510/attachment.html>


More information about the swift-users mailing list