Arrays of enums are enums in Swift?

2019-08-14 23:29发布

问题:

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?

回答1:

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


回答2:

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