I'm a bit confused by how UIControlState operates. Specifically, if I look at the following example:
sender.setTitle("NewTitle", for: UIControlState.normal)
I understand that this sets the title for the Button's (Sender's) normal state. I would have expected .normal to be an Enum value of the type UIControlState, but instead learned that it was a struct with constants. First question:
Secondly, when I look at the documentation for UIStateControl, all I see are definitions of "constants," eg:
static var normal: UIControlState
Second/Third question:
Why are the "constants" for UIStateControl defined with "var" and not "let?"
How is UIControlState's static property defined as type "UIControlState?" Isn't that recursive?
Why is UIControlState a Struct and not an Enum?
UIControlState
is fundamentally not an enumeration. An enumeration is an "OR" type.
enum Foo {
case a, b, c
}
var f = Foo.a
f = .b
f = .c
So, in the above example, f
can hold .a
or .b
or .c
.
But that's not the case with UIControlState
; it's an option set . An option set can hold a set of one or more cases. It is therefore an "AND" type, and we usually implement them with a struct
that conforms to the OptionSet
protocol.
struct Bar : OptionSet {
let rawValue: Int
// Note that the raw values are unique powers of two.
// Each bit represents a flag determining if a given case is present.
static let a = Bar(rawValue: 1) // 001
static let b = Bar(rawValue: 2) // 010
static let c = Bar(rawValue: 4) // 100
}
var b = Bar.a // .a but not .b or .c
b = [.a, .b] // .a and .b, but not .c
b = [.c, .a, .b] // .a and .b and .c
So, you can see in the above example, b
can hold any set of .a
, .b
and .c
.
And that's the same with UIControlState
; we can for example talk about a control state of being focused and highlighted:
let controlState: UIControlState = [.highlighted, .focused]
If it were an enum
, we could only talk about whether a control was in one specific state, such as only highlighted or only focussed. But that's not a correct model, because controls can be in multiple different states at once.
Also it's worth noting that with UIControlState
, the .normal
case is equivalent to an empty set of options []
; it means "not highlighted or focussed or selected or disabled or ...".
Secondly, when I look at the documentation for UIStateControl, all I see are definitions of "constants," eg:
static var normal: UIControlState
That's not quite accurate. The UIControlState
declaration in the auto-generated Swift header looks like this:
public struct UIControlState : OptionSet {
public init(rawValue: UInt)
public static var normal: UIControlState { get }
public static var highlighted: UIControlState { get } // used when UIControl isHighlighted is set
public static var disabled: UIControlState { get }
public static var selected: UIControlState { get } // flag usable by app (see below)
@available(iOS 9.0, *)
public static var focused: UIControlState { get } // Applicable only when the screen supports focus
public static var application: UIControlState { get } // additional flags available for application use
public static var reserved: UIControlState { get } // flags reserved for internal framework use
}
You'll note the { get }
at the end. This means that they are just read only properties. How they are actually implemented (as let
constants, var
read-only computed properties etc.) is a pure implementation detail.
In this case, UIControlState
is defined in UIKit with the NS_OPTIONS
macro, which Swift imports in as an OptionSet
conforming structure with each option value being a static read-only property.
How is UIControlState's static property defined as type "UIControlState?" Isn't that recursive?
No, it's not recursive at all. Remember, they're static
properties, and as such are nothing more than global variables namespaced to UIControlState
(and they don't even need to have storage; they could be computed, although, again that's an implementation detail).
If they were instance stored properties, then it would indeed be recursive. But they aren't.
Why is UIControlState a Struct and not an Enum?
Because UIControlEvents is related to the UIKit framework which has been built using Objective-C , means that it has nothing to do with the Swift enums.
If the purpose is to just pass a state, why wasn't this defined as an enum with .normal as one of the cases?
Nevertheless, UIControlEvents
struct conforms to OptionSet protocol, which is part of the Swift programming language; The purpose of conforming to the OptionSet
is to representing bit mask types . If you tried to create a structure which conforms to OptionSet
, you would notice that implementing init(rawValue:)
initializer and rawValue
property are required, example:
struct CustomOptions: OptionSet {
let rawValue: Int
static let easy = CustomOptions(rawValue: 0b00000001) // 1
static let medium = CustomOptions(rawValue: 0b00000010) // 2
static let hard = CustomOptions(rawValue: 0b00000011) // 3
static let unfair = CustomOptions(rawValue: 0b00000100) // 4
}
let myOption = CustomOptions.medium
print(myOption) // CustomOptions(rawValue: 2)
Note that the rawValues
usually are unique powers of two (1, 2, 4, 8, 16, and so forth), but I just made them 1, 2, 3 and 4 for the purpose of the simplicity.
Because the options have raw values, you will be able to call the option without even the need of mentioning what's the struct, consider the following:
func doSomething(param: CustomOptions) {
// ...
}
// you don't have to: doSomething(param: CustomOptions.medium)
// instead, you could call it like this:
doSomething(param: CustomOptions.medium)
Thus:
sender.setTitle("title", for: .normal)
Enum VS Structure conforming to OptionSet
But wait! why to use a structure that conforming to a OptionSet
protocol instead of an enum?
In addition to what mentioned related to the difference between the used programming languages, enum is a representation for a single value one at a time , but OptionSet structure could be a representation for combined values in a single value , sound confusing? consider the following:
let combinedOption: CustomOptions = [.easy, .medium]
print(combinedOption) // CustomOptions(rawValue: 3)
At the first look, [.easy, .medium]
might be tricky which looks like an array, but it is not! actually it is a single CustomOptions
instance which is a combination (summation) of easy
and medium
options.
Thus:
sender.setTitle("title", for: [.normal, .disabled])
Why are the "constants" for UIStateControl defined with "var" and not "let?"
It could be related to how the representation of the bridged Objective-C enumeration cases should be, however, if you would try to:
UIControlState.normal = 3
You should obviously get the error of:
which means that it has been declared (Objective-C) as a readonly property.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.