【问题标题】:How can I make a Swift enum with UIColor value?如何使用 UIColor 值创建 Swift 枚举?
【发布时间】:2016-08-24 01:02:07
【问题描述】:

我正在制作一个绘图应用程序,我想通过使用枚举来引用我的颜色。例如,使用Colors.RedColor 而不是每次我想要红色时都输入值会更干净、更方便。但是,Swift 的原始值枚举似乎不接受 UIColor 作为一种类型。有没有办法用枚举或类似的东西来做到这一点?

【问题讨论】:

  • “但是,Swift 的原始值枚举似乎不接受 UIColor 作为一种类型”正确。原始值枚举只能以字符串或数字文字作为值。

标签: swift enums


【解决方案1】:

我是这样做的(基本上使用结构作为命名空间):

extension UIColor {
  struct MyTheme {
    static var firstColor: UIColor  { return UIColor(red: 1, green: 0, blue: 0, alpha: 1) }
    static var secondColor: UIColor { return UIColor(red: 0, green: 1, blue: 0, alpha: 1) }
  }
}

你会像这样使用它:

UIColor.MyTheme.firstColor

因此,您可以在自定义主题中使用红色。

【讨论】:

  • 对我来说这是迄今为止最好的解决方案!
  • 如果它像枚举一样自动完成,那么它是最好的解决方案
  • 不做static var prop: Type { return Type.init() },你能做static var prop = Type.init()吗?
  • 将颜色初始化为这个静态变量 customGray: UIColor { return UIColor(red: 227.0 / 255.0, green: 227.0 / 255.0, blue: 227.0 / 255.0, alpha: 1.0) }
  • 这很棒。除此之外,我喜欢使用枚举作为命名空间,因为枚举不能被初始化,而结构可以。此解决方案将允许 MyTheme 被初始化,这不是该结构的重点,因为它充当命名空间并可能导致意外行为。
【解决方案2】:

如果您的颜色不是UIColor 的便捷方法定义的颜色之一,您可以向UIColor 添加扩展:

extension UIColor {
    static var firstColor: UIColor  { return UIColor(red: 1, green: 0, blue: 0, alpha: 1) }
    static var secondColor: UIColor { return UIColor(red: 0, green: 1, blue: 0, alpha: 1) }
}

// Usage
let myColor = UIColor.firstColor

【讨论】:

  • 事实上,在 Swift 3 中,内置颜色也是类变量,因此非常适合。
  • 这是问题所在:我不能这样做 static var red: UIColor 因为 UIColor 已经有一个 red 属性(在 Swift 3 中)。我可以将其称为 redColor 之类的其他名称,但同时拥有 redredColor 属性似乎是一种不好的做法
  • 那就别叫它red,如果你的颜色不是#FF0000,就叫它myRed
【解决方案3】:

我使用计算属性来解决这个问题,这是我的代码

enum MyColor {
    case navigationBarBackgroundColor
    case navigationTintCololr
}

extension MyColor {
    var value: UIColor {
        get {
            switch self {
            case .navigationBarBackgroundColor:
                return UIColor(red: 67/255, green: 173/255, blue: 247/255, alpha: 1.0)
            case .navigationTintCololr:
                return UIColor.white
            }
        }
    }
}

然后我可以像这样使用 MyColor:

MyColor.navigationBarBackgroundColor.value

【讨论】:

  • 这会在每次访问时创建一个新的UIColor 值,这可能会导致一些开销。
【解决方案4】:

如何使用 UIColor 值创建 Swift 枚举?

这就是你如何用 UIColor 值创建一个枚举:

import UIKit

final class Color: UIColor, RawRepresentable, ExpressibleByStringLiteral
{
    // MARK:- ExpressibleByStringLiteral

    typealias StringLiteralType = String

    convenience init(stringLiteral: String) {
        guard let (a,r,g,b) = Color.argb(hexColor: stringLiteral) else {
            assertionFailure("Invalid string")
            self.init(red: 0, green: 0, blue: 0, alpha: 0)
            return
        }
        self.init(red: r, green: g, blue: b, alpha: a)
    }

    // MARK:- RawRepresentable

    public typealias RawValue = String

    convenience init?(rawValue: RawValue) {
        guard let (a,r,g,b) = Color.argb(hexColor: rawValue) else { return nil }
        self.init(red: r, green: g, blue: b, alpha: a)
    }

    var rawValue: RawValue {
        return hexString()
    }

    // MARK:- Private

    /// Return color components in range [0,1] for hexadecimal color strings.
    /// - hexColor: case-insensitive string with format RGB, RRGGBB, or AARRGGBB.
    private static func argb(hexColor: String) -> (CGFloat,CGFloat,CGFloat,CGFloat)?
    {
        let hexAlphabet = "0123456789abcdefABCDEF"
        let hex = hexColor.trimmingCharacters(in: CharacterSet(charactersIn: hexAlphabet).inverted)
        var int = UInt32()
        Scanner(string: hex).scanHexInt32(&int)
        let a, r, g, b: UInt32
        switch hex.count {
        case 3: (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) // RGB
        case 6: (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) // RRGGBB
        case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) // AARRGGBB
        default: return nil
        }
        return (CGFloat(a)/255, CGFloat(r)/255, CGFloat(g)/255, CGFloat(b)/255)
    }

    private func hexString() -> String {
        var red:   CGFloat = 0
        var green: CGFloat = 0
        var blue:  CGFloat = 0
        var alpha: CGFloat = 0
        if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
            return String(format: "#%02X%02X%02X%02X", UInt8(red * 255), UInt8(green * 255), UInt8(blue * 255), UInt8(alpha * 255))
        }
        assertionFailure("Invalid colour space.")
        return "#F00"
    }
}

enum Colors: Color {
    case red = "#F00"
//    case blue = "#F00" // Raw value for enum case is not unique
}

let color3 = Color(rawValue: "#000") // RGB
let color6 = Color(rawValue: "#123456") // RRGGBB
let color8 = Color(rawValue: "#12345678") // AARRGGBB
print(Colors(rawValue:"#F00") as Any) // red
print(Colors(rawValue:"#FF0000") as Any) // red
print(Colors(rawValue:"#FFFF0000") as Any) // red
print(Colors(rawValue:"#ABC") as Any) // nil because it’s not a member of the enumeration
// print(Colors(rawValue:"#XYZ") as Any) // assertion on debug, black on release
print(Colors.red) // red
print(Colors.red.rawValue) // UIExtendedSRGBColorSpace 1 0 0 1

的帮助下

【讨论】:

    【解决方案5】:

    这可以做得更简洁(并且应该):

    extension UIColor
    {
        static let myColor = UIColor(displayP3Red: 0.0, green: 0.7, blue: 0.0, alpha: 1.0)
    }
    

    (任何其他返回 UIColor 的方法同样适用,不需要是displayP3Red

    用法:

    let someColor: UIColor = .myColor
    

    【讨论】:

    • 比每次都输入“MyTheme”要好得多。
    • 很好的解决方案。我不明白为什么人们在不必要的时候使用计算属性。我们基本上是这样做的,但在资产中使用了命名颜色。
    【解决方案6】:

    其实我用这样的实现,对我来说很方便,有两个原因,第一个我可以使用 dex 值,另一个我可以使用常量中的所有颜色

    import UIKit
    
    struct ColorPalette {
    struct Gray {
        static let Light = UIColor(netHex: 0x595959)
        static let Medium = UIColor(netHex: 0x262626)
    }
    }
    
    extension UIColor {
    convenience init(red: Int, green: Int, blue: Int) {
        assert(red >= 0 && red <= 255, "Invalid red component")
        assert(green >= 0 && green <= 255, "Invalid green component")
        assert(blue >= 0 && blue <= 255, "Invalid blue component")
    
        self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
    }
    
    convenience init(netHex: Int) {
        self.init(red: (netHex >> 16) & 0xff, green: (netHex >> 8) & 0xff, blue: netHex & 0xff)
    }
    }
    

    用法

    let backgroundGreyColor = ColorPalette.Gray.Medium.cgColor
    

    【讨论】:

      【解决方案7】:

      如果你想返回多个值然后使用下面的代码...绝对 为我工作....

      enum GetDriverStatus : String {
          case ClockIn            = "Clock In"
          case TripStart          = "Trip Start"
          case BeaconTouchPlant   = "Beacon Touch Plant"
          case PickUp             = "Pick Up"
          case BeaconTouchSite    = "Beacon Touch Site"
          case BeaconLeftSite     = "Beacon Left Site"
          case DropOff            = "Drop Off"
          case BreakIn            = "Break In"
          case BreakOut           = "Break Out"
          case TripEnd            = "Trip End"
          case DayEnd             = "Day End"
          //case ClockOut           = "Clock Out"
      
          //Get data from ID
          static var allValues: [GetDriverStatus] {
              return [
                  .ClockIn,
                  .TripStart,
                  .BeaconTouchPlant,
                  .PickUp,
                  .BeaconTouchSite,
                  .BeaconLeftSite,
                  .DropOff,
                  .BreakIn,
                  .BreakOut,
                  .TripEnd,
                  .DayEnd
              ]
          }
      
          //Get Color
          var colorAndStatus: (UIColor,String) {
              get {
                  switch self {
                  case .ClockIn,.TripStart: //Idle
                      return (UIColor(red: 248/255, green: 39/255, blue: 71/255, alpha: 1.0),"Idle") //dark pink-red
                  case .BeaconTouchPlant,.PickUp:
                      return (UIColor(red: 46/255, green: 180/255, blue: 42/255, alpha: 1.0),"Picking up") //Green
                  case .BeaconTouchSite:
                      return (UIColor(red: 252/255, green: 172/255, blue: 0/255, alpha: 1.0),"On site") //orange
                  case .DropOff,.BeaconLeftSite:
                      return (UIColor(red: 12/255, green: 90/255, blue: 255/255, alpha: 1.0),"Dropping off") //blue
                  case .BreakIn,.BreakOut:
                      return (UIColor(red: 151/255, green: 151/255, blue: 151/255, alpha: 1.0),"On break") //warm-grey-two
                  case .TripEnd:
                      return (UIColor.black,"Trip end")
                  case .DayEnd:
                      return (UIColor.black,"Done for the day")
                  }
              }
          }
      } 
      

      如何使用此代码 传递.allvalues["index of your option"] 你得到UIColor 在0 位置以及String value 作为1 位置

      GetDriverStatus.allValues[1].colorAndStatus.0 //UIColor.Black
      GetDriverStatus.allValues[2].colorAndStatus.1 //"Picking up"
      

      【讨论】:

        【解决方案8】:

        根据@Jano 的回答,我通过使用Int 作为文字类型进行了改进:

        import UIKit
        
        public final class Colors: UIColor {
        
        }
        
        extension Colors: ExpressibleByIntegerLiteral {
            public typealias IntegerLiteralType = Int
        
            public convenience init(integerLiteral value: Int) {
                let red = CGFloat((value & 0xFF0000FF) >> 24) / 0xFF
                let green = CGFloat((value & 0x00FF00FF) >> 16) / 0xFF
                let blue = CGFloat((value & 0x0000FFFF) >> 8) / 0xFF
                let alpha = CGFloat(value & 0x00FF00FF) / 0xFF
        
                self.init(red: red, green: green, blue: blue, alpha: alpha)
            }
        }
        
        extension Colors: RawRepresentable {
            public typealias RawValue = Int
        
            public var rawValue: RawValue {
                return hex
            }
        
            public convenience init?(rawValue: RawValue) {
                self.init(integerLiteral: rawValue)
            }
        }
        
        fileprivate extension UIColor {
            var hex: Int {
                var fRed: CGFloat = 0
                var fGreen: CGFloat = 0
                var fBlue: CGFloat = 0
                var fAlpha: CGFloat = 0
                if self.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha) {
                    let red = Int(fRed * 255.0)
                    let green = Int(fGreen * 255.0)
                    let blue = Int(fBlue * 255.0)
                    let alpha = Int(fAlpha * 255.0)
                    let rgb = (alpha << 24) + (red << 16) + (green << 8) + blue
                    return rgb
                } else {
                    return 0x000000
                }
            }
        }
        
        public enum MainPalette: Colors {
            case red = 0xFF0000ff
            case white = 0xFFFFFFFF
        }
        
        public enum FeatureXPalette: Colors {
            case blue = 0x024F9Eff
        //    case bluish = 0x024F9Eff // <- Can't do
            case red = 0xFF0000ff
        }
        

        优点是它不允许重复颜色(作为真正的枚举),而且我还支持 alpha。

        如您所见,您可以为不同的调色板/方案创建多个枚举。如果您希望视图能够使用任何调色板,您可以添加一个协议:

        protocol Color {
            var color: UIColor { get }
        }
        
        extension MainPalette: Color {
            var color: UIColor {
                return rawValue
            }
        }
        
        extension FeatureXPalette: Color {
            var color: UIColor {
                return rawValue
            }
        }
        

        这样你就可以拥有一个接受协议的函数:

        func printColorEquality(color1: Color, color2: Color) {
            print(color1.color == color2.color)
        }
        
        let red1: Color = MainPalette.red
        let red2: Color = FeatureXPalette.red
        
        printColorEquality(color1: red1, color2: red2)
        

        为了方便起见,我还喜欢添加静态变量:

        extension MainPalette {
            public static var brightRed: UIColor {
                return MainPalette.red.color
            }
        }
        

        给你一个更干净的 api:

        view.backgroundColor = MainPalette.brightRed
        

        可以改进命名:您必须选择是想要一个方便的 API 还是为枚举命名。

        【讨论】:

          【解决方案9】:

          这个答案可能已经晚了,但对于其他发现这个问题的人来说。 我对上面的答案不满意,因为添加颜色作为 UIColors 扩展并不总是你想要的,因为:

          1. 从软件架构的角度来看,这可能不是最佳解决方案。
          2. 您不能使用枚举具有的权力,例如CaseIterable

          这是我想出的解决方案:

          enum PencilColor {
              case lightRed
              case darkPurple
              
              var associatedColor: UIColor {
                  switch self {
                  case .lightRed: return UIColor(red: 67/255, green: 173/255, blue: 247/255, alpha: 1.0)
                  case .darkPurple: return UIColor(red: 67/255, green: 173/255, blue: 247/255, alpha: 1.0)
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-02-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-01
            相关资源
            最近更新 更多