Swift3 Extension on “Any?” -ish class?

旧时模样 提交于 2021-01-29 03:15:26

问题


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 (your v) parameter
  • While, for a UIView extension, you don't need v as this will be self; it doesn't make sense to install a gesture recogniser on some other UIView.
  • 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!