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 <[email protected]>:
>
> 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 <[email protected]
> <mailto:[email protected]>> 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 <[email protected]
> <mailto:[email protected]>> 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 <[email protected]
> <mailto:[email protected]>> 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 <[email protected]
> <mailto:[email protected]>> 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
>> <[email protected] <mailto:[email protected]>> 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 <[email protected]
>> <mailto:[email protected]>> 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
>> <[email protected] <mailto:[email protected]>> 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
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-users
>> <https://lists.swift.org/mailman/listinfo/swift-users>
>>
>>
>>
>>
>>
>> --
>> Nate Birkholz
>> _______________________________________________
>> swift-users mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-users
>> <https://lists.swift.org/mailman/listinfo/swift-users>
>
>
>
> --
> Nate Birkholz
>
>
>
> --
> Nate Birkholz
>
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users