在 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
。
例如,
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
。