Search code examples
swiftcocoa-touchenumsviewwithtag

Using a Swift enum as view tag numbers without rawValue


I have an integer enum which I'd like to use for the viewWithTag(_:) number, but it gives me the error "Cannot convert value of type 'viewTags' to expected argument type 'Int'", even though both the enum and the tag number needed in viewWithTag(_:) is an Int.

This is pretty simple, and I can get it to work if I use the rawValue property, but that's more messy and cumbersome than I'd like.

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()
}

Solution

  • You can easily add an extension on UIView to do the conversion for you. You just need to use a generic parameter to restrict the argument to something that you can get an Int from.

    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)
        }
    }
    

    The constraint T : RawRepresentable where T.RawValue == Int can be fulfilled by your Int-backed enum.

    An non-generic form is easy too: func firstView(taggedBy viewTag: ViewTag) -> UIView?

    Bonus, you can also add a method to apply the raw value of a "composed" value to the view's:

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

    (Unfortunately there's no way to write that as a property, e.g. var composedTag: Tag where Tag : RawRepresentable, Tag.RawValue == Int because a computed property can't create its own generic context like the method can.)