Reputation: 2466
I have an extension on UIView that returns an array of sub-views based on meta type. This way I can query the view hierarchy for example for all UITextFields, etc.
extension UIView {
func subviewsOfType<T:UIView>(_:T.Type) -> [T]? {
...
}
}
This can be called like so:
let fields = loginView.subviewsOfType(UITextField.self)
Now, I would like to act on that array [UITextField]
with an interface that matches the generic passed in, in other words whatever is available on UITextField, example:
fields.alpha = 0.3 // via UIView
fields.enabled = false // via UIControl
I have created extensions on MutableCollectionType
to provide these overrides:
extension MutableCollectionType where Generator.Element == UIView {
var alpha:CGFloat {
get {
if let first = self.first {
return first.alpha
} else {
return 1.0
}
}
set {
for view in self {
view.alpha = newValue
}
}
}
}
extension MutableCollectionType where Generator.Element == UIControl {
var enabled:Bool {
get {
if let first = self.first {
return first.enabled
} else {
return true
}
}
set {
for view in self {
view.enabled = newValue
}
}
}
}
However, when I try to use these extensions they do not work as expected. :(
Here are the variations of calling code I have tried and their errors:
Alternatively, I tried using methods instead of computed properties, e.g.
func alpha(alpha:CGFloat) -> Self {
for view in self {
view.alpha = alpha
}
return self
}
This "works" but includes ugly casting:
I would prefer to use regular properties on those arrays via the MutableCollectionType
extensions:
loginView.subviewsOfType(UITextField.self).enabled = true
What am I missing??
Upvotes: 3
Views: 286
Reputation: 539815
There are two problems. As Rob already said, you have to restrict
the extension methods to subclasses of UIView
/UIControl
.
But then the compiler still complains on
if let fields = self.view.subviewsOfType(UITextField.self) {
fields.alpha = 0.3
fields.enabled = false
}
with
error: cannot assign to property: 'fields' is a 'let' constant
The reason is that setting a property is considered a mutation of the array (which is a value type), and you cannot mutate constants.
However, the array elements itself in your case are reference types
(instances of a class). The setter methods mutate only the referenced objects, and leave the
array itself unchanged. Therefore you can declare the setter method
as nonmutating
:
var alpha:CGFloat {
get { ... }
nonmutating set {
for view in self {
view.alpha = newValue
}
}
}
And now the above code compiles and works without problems.
Upvotes: 4
Reputation: 299355
You've constrained to exactly UIView
, not subclasses of UIView
. You meant this:
extension MutableCollectionType where Generator.Element: UIView { ... }
extension MutableCollectionType where Generator.Element: UIControl { ... }
Note the :
rather than ==
.
And in this particular case, you'll need fields
to be a var
since you mutate it:
if var fields = loginView.subviewsOfType(UITextField.self) {
fields.alpha = 0.3
fields.enabled = true
}
Upvotes: 4