问题
Shorter explanation:
You often want to extend on "target" ... and targets are usually Any?
. But you can't have an extension on Any
. How to do it?
Consider this,
extension UIViewController {
func add(tap v:UIView, _ action:Selector) {
let t = UITapGestureRecognizer(target: self, action: action)
v.addGestureRecognizer(t)
}
}
Excellent, you can now...
self.tap(redButton, #selector(clickedRedButton))
... in any view controller.
But you can do the same thing to just about any target.
So, to use the extension on a UITableViewCell say, you have to also have....
extension UIGestureRecognizerDelegate {
func add(tap v:UIView, _ action:Selector) {
let t = UITapGestureRecognizer(target: self, action: action)
v.addGestureRecognizer(t)
}
}
The target argument of UITapGestureRecognizer is actually Any?
But, you can not do this ...
extension Any {
What's the solution? How to make an extension that will work on the Any?
, as for example in the first argument of UITapGestureRecognizer
?
Or as Conner'c comment suggests, is there a way to:
extension UIViewController or UIView {
rather than copying and pasting it twice?
回答1:
"Any" is adhered to (passively) by every struct/class. An extension to Any would add that functionality to every single type in the language and your code. This isn't currently possible, and I doubt it ever would be (or should be).
Anyway, here are a few ways to solve this problem.
My preference is a protocol extension that adds the functionality:
protocol TapGestureAddable {
func addTapGestureRecognizer(to view: UIView, with action: Selector) -> UITapGestureRecognizer
}
extension TapGestureAddable {
func addTapGestureRecognizer(to view: UIView, with action: Selector) -> UITapGestureRecognizer {
let recognizer = UITapGestureRecognizer(target: self, action: action)
view.addGestureRecognizer(recognizer)
return recognizer
}
}
extension UIViewController: TapGestureAddable { }
extension UIView: TapGestureAddable { }
This forces you to knowingly choose to add the functionality to a given class, (a good thing IMO) without having to duplicate any meaningful code.
Possibly a better option would be to make this logic an extension of UIView instead:
extension UIView {
func addTapGestureRecognizer(with responder: Any, for action: Selector) -> UITapGestureRecognizer {
let recognizer = UITapGestureRecognizer(target: responder, action: action)
self.addGestureRecognizer(recognizer)
return recognizer
}
func addTapGestureRecognizer(with action: Selector) -> UITapGestureRecognizer {
let recognizer = UITapGestureRecognizer(target: self, action: action)
self.addGestureRecognizer(recognizer)
return recognizer
}
}
Otherwise, just make a global function:
func addTapGestureRecognizer(to view: UIView, with responder: Any, for action: Selector) -> UITapGestureRecognizer {
let recognizer = UITapGestureRecognizer(target: responder, action: action)
view.addGestureRecognizer(recognizer)
return recognizer
}
回答2:
Any
isn't a class in the way that NSObject
is. It is merely a keyword that indicates to the Swift compiler that a variable/constant/parameter may refer to any object or struct instance, so it isn't possible to extend Any
.
If you consider what you are trying to do, you would have a subtle difference between your two extensions anyway;
- The
UIViewController
extension needs to accept a target view (yourv
) parameter - While, for a
UIView
extension, you don't needv
as this will beself
; it doesn't make sense to install a gesture recogniser on some otherUIView
. - For the
UIView
extension, you may want to specify a different target for the selector. - You don't add a gesture recogniser to the UIViewController, so it doesn't make, semantically, to extend
UIViewController
in this way.
So, to me, it seems that the logical extension looks somthing like:
extension UIView {
func add(_ action:Selector,tapHandler target:Any = self) {
let t = UITapGestureRecognizer(target: target, action: action)
self.addGestureRecognizer(t)
}
}
Now, in a UIViewController
you can say something like:
self.redButton.add(Selector(("handleTap")), tapHandler: self)
While in a UIView
subclass you can say:
self.add(Selector(("handleTap")))
来源:https://stackoverflow.com/questions/41641619/swift3-extension-on-any-ish-class