I'm following Apple's Swift/iOS tutorial and it features this line of code:
button.setImage( filledStarImage, forState: [UIControlState.Highlighted, UIControlState.Selected] )
But the definition of UIButton.setImage
's signature is:
public func setImage(image: UIImage?, forState state: UIControlState)
There is no overload of setImage
that accepts an [UIControlState]()
array.
Obviously some voodoo is going on, can anyone explain?
In Swift, the UIControlState
is not, per se, an enum, but actually a structure. From the language reference for UIControl
:
Control State
The state of a control; a control can have more than one state at a
time. States are recognized differently depending on the control. For
example, a UIButton instance may be configured (using the
setImage:forState: method) to display one image when it is in its
normal state and a different image when it is highlighted.
Declaration
struct UIControlState : OptionSetType {
init(rawValue rawValue: UInt)
static var Normal: UIControlState { get }
static var Highlighted: UIControlState { get }
static var Disabled: UIControlState { get }
static var Selected: UIControlState { get }
static var Focused: UIControlState { get }
static var Application: UIControlState { get }
static var Reserved: UIControlState { get }
}
The key here is the OptionSetType
protocol (to which UIControlState
conforms), which allows us to do "enum-like" access to the static structures properties, given that these have a RawValue
that is a BitwiseOperationsType
. E.g.:
struct MyStruct : OptionSetType {
private var value: UInt
var rawValue: UInt { return self.value }
init(rawValue value: UInt) { self.value = value }
static var Normal: MyStruct { return self.init(rawValue: 1 << 0) }
static var Highlighted: MyStruct { return self.init(rawValue: 1 << 1) }
static var Disable: MyStruct { return self.init(rawValue: 1 << 2) }
// ...
}
let MyOptionsA : MyStruct = [MyStruct.Normal, MyStruct.Disable]
let MyOptionsB : MyStruct = [.Normal, .Disable]
MyOptionsB.contains(.Highlighted) // false
MyOptionsB.contains(.Normal) // true
Have a look at the instance methods (see link to language ref below) made available by conforming to the OptionsSetType
. In addition membership management (e.g. .contains(...)
as above; also, .remove(...)
, .insert(..)
), neat set operations such as union(...)
and intersect(...)
are also available.
So to summarize; you are right that the UIButton.setImage
signature asks for forState state: UIControlState
, but, due to the OptionSetType
protocol, the array-looking [UIControlState.Highlighted, UIControlState.Selected]
is in fact a valid argument for call to structure type parameter UIControlState
.
Note also, as per my example above, that the structure name can be left out, so sending [.Highlighted, .Selected]
as forState
argument would be just as fine.
Reference & good reads:
- OptionSetType Protocol Language Reference
- How do you enumerate OptionSetType in Swift?
- http://www.informit.com/articles/article.aspx?p=2420231
UIControlState conforms to the OptionSetType protocol, which provides convenience methods for handling BitwiseOperationsTypes. So it's not setImage that's being overloaded, but what looks like an array. See https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_OptionSetType_Protocol/index.html#//apple_ref/swift/intf/s:PSs13OptionSetType