Reputation: 12590
For example,
superview?.subviews.filter{
$0 != self &&
$0.responds(to: #selector(setter: Blueable.blue))
}.map{
($0 as! Blueable).blue = false
}
Is there a concept like ..
x.blue??? = false
'???' meaning 'if it responds to blue, call blue'...
Note - I fully appreciate I could write an extension, callIfResponds:to
, or a specific extension, blueIfBlueable
.
I'm wondering if there's some native swiftyness here, which I don't know about. It seems to be a pretty basic concept.
Footnote:
in the ensuing heated discussion, there is mention of using a protocol. Just for the benefit of anyone reading, here's one approach to using a protocol:
protocol Blueable:class {
var blue:Bool { get set }
}
extension Blueable where Self:UIView {
func unblueAllSiblings() { // make this the only blued item
superview?.subviews.filter{$0 != self}
.flatMap{$0 as? Blueable}
.forEach{$0.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)
To use it, for example...
@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
}
}
}
}
Just going back to the original question, that's all fine. But you can't "pick up on" an existing property (a simple example is isHidden
) in existing classes.
Furthermore as long as we're discussing it, note that in that example protocol extension, you unfortunately can NOT have the protocol or extension automatically, as it were, call unblueAllSiblings from "inside" the protocol or extension, for exactly this reason: why you can't do it
Upvotes: 2
Views: 826
Reputation: 2459
Why not check conformance to the type directly? Something like:
superview?.subviews.forEach {
guard $0 !== self else { return }
($0 as? Blueable)?.blue = false
}
Upvotes: 2
Reputation: 80791
As others have said, a better way of going about this is by conditionally type-casting rather than using responds(to:)
. However, don't overlook just using a for in
loop – they're pretty powerful, allowing you to use pattern matching and where
clauses, allowing you to iterate over a given subset of elements.
for case let blueable as Blueable in superview?.subviews ?? [] where blueable !== self {
blueable.blue = false
}
case let blueable as Blueable
uses the type-casting pattern in order to only match elements that are Blueable
, and bind them to blueable
if successful.
where blueable !== self
excludes self
from being matched.
Upvotes: 4
Reputation: 59496
You can add this extension
extension UIView {
var siblings: [UIView] { return self.superview?.subviews.filter { $0 != self } ?? [] }
}
Now pick the solution that you prefer among the followings
siblings.flatMap { $0 as? Blueable }.forEach { $0.blue = false }
siblings.forEach { ($0 as? Blueable)?.blue = false }
Upvotes: 1
Reputation: 130102
Somehow I think what you want is:
superview?.subviews.filter {
$0 != self // filter out self
}.flatMap {
$0 as? Blueable
}.forEach {
$0.blue = false
}
Why should you be checking whether a class conforms to a setter when you can check the type?
Checking selectors should not be used in pure Swift. You will need it mostly for interacting with Obj-C APIs - dynamic methods, optional methods in protocols or informal protocols.
Upvotes: 2