在 Swift3 中,一口气结合响应#to 和调用?

In Swift3, combine responds#to and calling in one fell swoop?

例如,

superview?.subviews.filter{
    [=10=] != self &&
    [=10=].responds(to: #selector(setter: Blueable.blue))
  }.map{
    ([=10=] as! Blueable).blue = false
  }

有没有像..这样的概念

x.blue??? = false

'???'意思是 'if it responds to blue, call blue'...

注意 - 我非常感谢我可以写一个扩展,callIfResponds:to,或一个特定的扩展,blueIfBlueable

我想知道这里是否有一些我不知道的本地敏捷性。这似乎是一个非常基本的概念。


脚注:

在随后的热烈讨论中,提到了使用协议。为了任何阅读者的利益,这里有一种使用协议的方法:

protocol Blueable:class {
    var blue:Bool { get set }
}

extension Blueable where Self:UIView {
    func unblueAllSiblings() { // make this the only blued item
        superview?.subviews.filter{[=12=] != self}
            .flatMap{[=12=] as? Blueable}
            .forEach{[=12=].blue = false}
    }
}

// explanation: anything "blueable" must have a blue on/off concept.
// you get 'unblueAllSiblings' for free, which you can call from
// any blueable item to unblue all siblings (likely, if that one just became blue)

使用它,例如...

@IBDesignable
class UILabelStarred: UILabel, Blueable {

    var blueStar: UIView? = nil
    let height:CGFloat = 40
    let shinyness:CGFloat = 0.72
    let shader:Shader = Shaders.Glossy
    let s:TimeInterval = 0.35

    @IBInspectable var blue:Bool = false {
        didSet {
            if (blue == true) { unblueAllSiblings() }
            blueize()
        }
    }

    func blueize() {

        if (blueStar == nil) {
            blueStar = UIView()
            self.addSubview(blueStar!)
            ... draw, say, a blue star here
            }
        if (blue) {
            UIView.animate(withDuration: s) {
                self. blueStar!.backgroundColor = corporateBlue03
                self.textColor = corporateBlue03
            }
        }
        else {
            UIView.animate(withDuration: s) {
                self. blueStar!.backgroundColor = UIColor.white
                self.textColor = sfBlack5
            }
        }
    }
}

回到原来的问题,没关系。但是你不能在现有的 类.

中 "pick up on" 现有的 属性(一个简单的例子是 isHidden

此外,只要我们正在讨论它,请注意,在该示例协议扩展中,不幸的是,您不能自动拥有协议或扩展,就像从 "inside" 协议或扩展中调用 unblueAllSiblings ,正是出于这个原因:

不知何故我想你想要的是:

superview?.subviews.filter {
   [=10=] != self // filter out self
}.flatMap {
  [=10=] as? Blueable
}.forEach {
  [=10=].blue = false
}

既然可以检查类型,为什么还要检查 class 是否符合 setter?

不应在纯 Swift 中使用检查选择器。您将主要需要它来与 Obj-C API 交互 - 动态方法、协议或非正式协议中的可选方法。

为什么不直接检查类型的一致性?类似于:

superview?.subviews.forEach {
    guard [=10=] !== self else { return }
    ([=10=] as? Blueable)?.blue = false
}

您可以添加此扩展程序

extension UIView {
    var siblings: [UIView] { return self.superview?.subviews.filter { [=10=] != self } ?? [] }
}

现在从以下选项中选择您喜欢的解决方案

解决方案 1

siblings.flatMap { [=11=] as? Blueable }.forEach { [=11=].blue = false  }

解决方案 2

siblings.forEach { ([=12=] as? Blueable)?.blue = false }

正如其他人所说,更好的解决方法是有条件地 type-casting 而不是使用 responds(to:)。但是,不要忽视仅使用 for in 循环——它们非常强大,允许您使用模式匹配和 where 子句,允许您迭代给定的元素子集。

for case let blueable as Blueable in superview?.subviews ?? [] where blueable !== self {
    blueable.blue = false
}
  • case let blueable as Blueable 使用 type-casting pattern 以便仅匹配 Blueable 的元素,如果成功则将它们绑定到 blueable

  • where blueable !== self 从匹配中排除 self