使用 Swift 枚举作为不带 rawValue 的视图标签编号

Using a Swift enum as view tag numbers without rawValue

我有一个整数枚举,我想将其用于 viewWithTag(_:) 数字,但它给我错误 "Cannot convert value of type 'viewTags' to expected argument type 'Int'",即使枚举和标签号都需要 viewWithTag(_:) 是一个 Int.

这很简单,如果我使用 rawValue 属性 就可以让它工作,但那比我想要的更混乱和麻烦。

enum viewTags: Int {
    case rotateMirroredBtn
    case iPhone6SP_7P_8P
    case iPhoneX_Xs
    case iPhoneXs_Max
    case iPhone_Xr
    case unknown
}

// error on if statement "Cannot convert value of type 'viewTags' to expected argument type 'Int'"
if let tmpButton = self.view.viewWithTag(viewTags.rotateMirroredBtn) as? UIButton { 
    tmpButton.removeFromSuperview()
}

您可以轻松地在 UIView 上添加扩展程序来为您进行转换。您只需要使用通用参数将参数限制为可以从中获得 Int 的参数。

extension UIView
{
    /**
     Returns the view’s nearest descendant (including itself) whose `tag`
     matches the raw value of the given value, or `nil` if no subview
     has that tag.
     - parameter tag: A value that can be converted to an `Int`.
     */
    func firstView <Tag : RawRepresentable> (taggedBy tag: Tag) -> UIView?
        where Tag.RawValue == Int
    {
        let intValue = tag.rawValue
        return self.viewWithTag(intValue)
    }
}

约束 T : RawRepresentable where T.RawValue == Int 可以通过 Int 支持的枚举来实现。

非通用形式也很简单:func firstView(taggedBy viewTag: ViewTag) -> UIView?

奖金,您还可以添加一个方法来 apply 将 "composed" 值的原始值应用到视图的:

func applyTag <Tag : RawRepresentable> (_ tag: Tag)
    where Tag.RawValue == Int
{
    self.tag = tag.rawValue
}

(不幸的是,无法将其写成 属性,例如 var composedTag: Tag where Tag : RawRepresentable, Tag.RawValue == Int,因为计算的 属性 无法像方法那样创建自己的通用上下文。)

我和最初的发帖人一样,不喜欢在代码中使用案例的 rawValue,因此我将计算类型属性添加到我的枚举中。我正在使用 Xcode v11.3.1 和 Swift v5.1.3.

例如,我编写的许多单元测试都使用 "magic" 值来为 table 视图创建 IndexPath,代码如下:

let activeIndexPath = IndexPath(row: 0, section: 0)
let finishedIndexPath = IndexPath(row: 0, section: 1)

我不想这样做,尽管它比 "magic" 值有所改进:

let activeIndexPath = IndexPath(row: 0, section: TableViewSection.active.rawValue)
let finishedIndexPath = IndexPath(row: 0, section: TableViewSection.finished.rawValue)

我最关心的是我正在测试的 table 视图部分,所以我想出了这个枚举,它使用计算类型属性来获取 Int rawValues:

enum TableViewSection: Int {
    case active
    case finished

    static var sectionActive: Int { return Self.active.rawValue }
    static var sectionFinished: Int { return Self.finished.rawValue }
}

现在我可以像这样创建一个 IndexPath:

let indexPathActive = IndexPath(row: 0, section: TableViewSection.sectionActive)

缺点是您需要为每个案例使用相似名称的计算 属性,但最终结果在调用站点更具描述性(尽管我猜使用 rawValue 的代码也具有描述性) ,现在我不必记住 table 视图的每个特定部分要使用哪个 Int 值,而且我不必再使用 "magic" 值,我们都知道这是不好的东西。

希望对您有所帮助!