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 <[email protected]>:
>
> 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]
>> <mailto:[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