【问题标题】:Why is UIControlState a Struct and not an Enum?为什么 UIControlState 是结构而不是枚举?
【发布时间】:2017-09-25 07:05:53
【问题描述】:

我对 UIControlState 的运作方式有些困惑。具体来说,如果我看下面的例子:

sender.setTitle("NewTitle", for: UIControlState.normal) 

我知道这会设置按钮(发件人)正常状态的标题。我本来希望 .normal 是 UIControlState 类型的 Enum 值,但后来得知它是一个带有常量的结构。第一个问题:

  1. 如果目的只是传递一个状态,为什么不将其定义为以 .normal 作为其中一种情况的枚举?

其次,当我查看 UIStateControl 的文档时,我看到的只是“常量”的定义,例如:

static var normal: UIControlState

第二/第三题:

  1. 为什么 UIStateControl 的“常量”定义为“var”而不是“let”?

  2. UIControlState 的静态属性如何定义为“UIControlState”类型?这不是递归的吗?

【问题讨论】:

  • 我对此不确定,但如果你看到 struct public struct UIControlState : OptionSet OptionSet 是 RowRepresntable ,那么 .normal 就可以了,@Paulw11 可能会对此有所帮助
  • @Paulw11 这不是由于 Obj-C 的任何限制;用NS_ENUM 宏定义的枚举作为enums 导入到Swift 中。这纯粹是因为UIControlState 根本不是enum;这是一个选项集。

标签: ios swift


【解决方案1】:

为什么 UIControlState 是 Struct 而不是 Enum?

UIControlState 从根本上说不是枚举。枚举是“OR”类型。

enum Foo {
    case a, b, c
}

var f = Foo.a
f = .b
f = .c

所以,在上面的例子中,f 可以持有.a .b .c

UIControlState 的情况并非如此;这是一个选项集。一个选项集可以包含一组一个或多个案例。因此它是一个“AND”类型,我们通常使用符合OptionSet 协议的struct 来实现它们。

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

因此,您可以在上面的示例中看到,b 可以包含.a.b.c 的任意集合。

UIControlState 也是如此;例如,我们可以谈论被聚焦突出显示的控制状态:

let controlState: UIControlState = [.highlighted, .focused]

如果是enum,我们只能谈论控件是否处于一个特定状态,例如仅突出显示仅聚焦。但这不是一个正确的模型,因为控件可以同时处于多个不同的状态。

另外值得注意的是,对于UIControlState.normal 的情况相当于一个空的选项集[];它的意思是“未突出显示或聚焦或选择或禁用或......”。

其次,当我查看 UIStateControl 的文档时,我 请参阅“常量”的定义,例如:

static var normal: UIControlState

这不太准确。自动生成的 Swift 标头中的 UIControlState 声明如下所示:

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
}

您会在末尾看到{ get }。这意味着它们只是 只读 属性。它们的实际实现方式(如let 常量、var 只读计算属性等)是纯粹的实现细节。

在这种情况下,UIControlState 在 UIKit 中使用 NS_OPTIONS 宏定义,Swift imports in as an OptionSet 符合结构,每个选项值都是静态只读属性。

UIControlState 的静态属性如何定义为“UIControlState”类型?这不是递归的吗?

不,它根本不是递归的。请记住,它们是static 属性,因此只不过是命名空间为UIControlState 的全局变量(它们甚至不需要存储;它们可以被计算,不过,这又是一个实现细节)。

如果它们是 instance 存储属性,那么它确实是递归的。但他们不是。

【讨论】:

    【解决方案2】:

    为什么 UIControlState 是 Struct 而不是 Enum?

    因为UIControlEvents与使用Objective-C构建的UIKit框架相关,意味着它与Swift枚举无关。


    如果目的只是传递一个状态,为什么不将其定义为 以 .normal 作为案例之一的枚举?

    尽管如此,UIControlEvents 结构符合OptionSet 协议,它是 Swift 编程语言的一部分;符合OptionSet 的目的是代表bit mask types。如果您尝试创建符合OptionSet 的结构,您会注意到需要实现init(rawValue:) 初始化器和rawValue 属性,例如:

    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)
    

    请注意,rawValues 通常是 2 的唯一幂(1、2、4、8、16 等),但我只是将它们设为 1、2、3 和 4,目的是简单。

    由于选项具有原始值,您将能够调用选项而无需提及结构是什么,请考虑以下事项:

    func doSomething(param: CustomOptions) {
        // ...
    }
    
    // you don't have to: doSomething(param: CustomOptions.medium)
    // instead, you could call it like this:
    doSomething(param: CustomOptions.medium)
    

    因此:

    sender.setTitle("title", for: .normal)
    

    符合 OptionSet 的枚举 VS 结构

    但是等等!为什么要使用符合OptionSet 协议的结构而不是枚举?

    除了提到的与所用编程语言之间的差异有关的内容之外,枚举是一次一个值的表示,但 OptionSet 结构可以是组合的表示单个值中的值,听起来令人困惑?考虑以下几点:

    let combinedOption: CustomOptions = [.easy, .medium]
    print(combinedOption) // CustomOptions(rawValue: 3)
    

    乍一看,[.easy, .medium] 看起来像一个数组可能很棘手,但事实并非如此!实际上它是一个 CustomOptions 实例,它是 easymedium 选项的组合(总和)。

    因此:

    sender.setTitle("title", for: [.normal, .disabled])
    


    为什么 UIStateControl 的“常量”定义为“var”而不是 “让?”

    这可能与桥接的 Objective-C 枚举案例的表示方式有关,但是,如果您尝试这样做:

    UIControlState.normal = 3
    

    你显然应该得到以下错误:

    这意味着它已被声明为 (Objective-C) 为 只读 属性。

    【讨论】:

    • 感谢您的解释您知道为什么static var normal: UIControlState 吗?而不是let
    • my above comment; UIControlState 在 Obj-C 中定义的事实与为什么它没有作为 enum 导入 Swift 无关。这纯粹是因为UIControlState 根本不是enum
    猜你喜欢
    • 2011-03-11
    • 1970-01-01
    • 2018-07-22
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    • 2012-10-28
    • 2023-04-01
    • 2013-12-30
    相关资源
    最近更新 更多