swift下优雅的设置颜色 ColorProvider

2,547 阅读2分钟

前言

iOS开发中,经常要跟UI打交道,颜色设置使用频率非常高。在OC中,我们经常使用宏来设置颜色。在Swift中,我们可以利用Swift的语言特性,面向协议,结体体、类、枚举可以遵守协议并实现,同时利用Swift语言协议的默认实现,简化代码,使用属性包装器,限制参数合理的取值范围

常用颜色

  • RGB,RGBA
  • HEX,HEX+透明度

使用姿势

image.png

实现,从一个协议开始

public protocol ColorProvider {
    var uiColor: UIColor { get }
    var cgColor: CGColor { get }
}

实现结构

image.png

其它处理

字符串颜色截取处理

extension String {
    func getColor(alpha: CGFloat) -> UIColor {
        let set: CharacterSet = CharacterSet.whitespacesAndNewlines
        var cstr = trimmingCharacters(in: set).uppercased() as NSString
        if cstr.length < 6 { return .clear }
        if cstr.hasPrefix("0X") {
            cstr = cstr.substring(from: 2) as NSString
        } else if cstr.hasPrefix("#") {
          cstr = cstr.substring(from: 1) as NSString
        }
        if cstr.length != 6 { return .clear }
        var range = NSRange()
        range.location = 0
        range.length = 2
        // r
        let rStr = cstr.substring(with: range);
        // g
        range.location = 2
        let gStr = cstr.substring(with: range)
        // b
        range.location = 4
        let bStr = cstr.substring(with: range)
        var r: UInt32 = 0x0, g: UInt32 = 0x0, b: UInt32 = 0x0
        Scanner(string: rStr).scanHexInt32(&r)
        Scanner(string: gStr).scanHexInt32(&g)
        Scanner(string: bStr).scanHexInt32(&b)
        return UIColor(
            red: CGFloat(r) / 255.0,
            green: CGFloat(g) / 255.0,
            blue: CGFloat(b) / 255.0,
            alpha: alpha
        )
    }
}

给UIColor扩展一些常用的方法

/// rgb颜色,不用再除255, 使用例子:  UIColor(red: 129, green: 21, blue: 12)
///
/// - Parameters:
///   - red: red component.
///   - green: green component.
///   - blue: blue component.
///   - transparency: optional transparency value (default is 1).
convenience public init?(red: Int,
                         green: Int,
                         blue: Int,
                         transparency: CGFloat = 1) {
    guard red >= 0 && red <= 255 else { return nil }
    guard green >= 0 && green <= 255 else { return nil }
    guard blue >= 0 && blue <= 255 else { return nil }
    
    var trans = transparency
    if trans < 0 { trans = 0 }
    if trans > 1 { trans = 1 }

    self.init(
        red: CGFloat(red) / 255.0,
        green: CGFloat(green) / 255.0,
        blue: CGFloat(blue) / 255.0,
        alpha: trans
    )
}

/// 随机色
public static var random: UIColor {
    let range: ClosedRange = 0...255
    let red = Int.random(in: range)
    let green = Int.random(in: range)
    let blue = Int.random(in: range)
    return UIColor(red: red, green: green, blue: blue)!
}

属性包装器限制取值范围,提高严谨性

RGB(0~255)取值范围

 @propertyWrapper  public struct RGBRange<Type: Comparable> {
    private var value: Type     private var min: Type     private var max: Type     
    public var wrappedValue: Type {
        get { value }
        set {
            value = (min < newValue) ? (max > newValue ? newValue : max) : min
        }
    }
    
    public init(wrappedValue: Type, min: Type = 0, max: Type = 255) {
        #if DEBUG
        assert((min <= wrappedValue && max >= wrappedValue), "\nRGB值(wrappedValue)不在范围 [(min), (max)] 区间范围内")
        #endif
        self.value = wrappedValue
        self.min = min
        self.max = max
    }
}

alpha(0.0~1.0),取值范围

 @propertyWrapper  public struct AlphaRange {
    private var value: CGFloat
    private var min: CGFloat
    private var max: CGFloat
    
    public var wrappedValue: CGFloat {
        get { value }
        set {
            value = (min < newValue) ? (max > newValue ? newValue : max) : min
        }
    }
    
    public init(wrappedValue: CGFloat, min: CGFloat = 0.0, max: CGFloat = 1.0) {
        #if DEBUG
        assert((min <= wrappedValue && max >= wrappedValue), "alpha值 \n(wrappedValue)不在范围 [(min), (max)] 区间范围内")
        #endif
        self.value = wrappedValue
        self.min = min
        self.max = max
    }
}

使用时,用自定义属性包装器修饰

/// RGB 颜色
public struct RGBAColor {
    @RGBRange public var red: Int
    @RGBRange public var green: Int
    @RGBRange public var blue: Int
    @AlphaRange public var alpha: CGFloat
    public init(red: Int,
                green: Int,
                blue: Int,
                alpha: CGFloat = 1.0) {
        self.alpha = alpha
        self.blue = blue
        self.green = green
        self.red = red
    }
}

暗黑模式适配

浅色模式和深色模式下,设置动态颜色

/// 设置动态颜色,直接设置Int类型的hex值
/// - Parameter light: 浅色模式下的颜色
/// - Parameter dark: 深色模式下的颜色
public static func dynamic(_ light: ColorProvider,
                           dark: ColorProvider,
                           alphaComponent alpha: CGFloat? = nil) -> UIColor {
    if #available(iOS 13.0, *) {
        return UIColor.init { (traitCollection) -> UIColor in             let notLight = traitCollection.userInterfaceStyle != .light
            if let alpha = alpha {
                let darkColor = dark.uiColor.withAlphaComponent(alpha)
                let lightColor = light.uiColor.withAlphaComponent(alpha)
                return notLight ? darkColor : lightColor
            }
            let darkColor = dark.uiColor
            let lightColor = light.uiColor
            return notLight ? darkColor : lightColor
        }
    } else {
        if let alpha = alpha {
            return light.uiColor.withAlphaComponent(alpha)
        }
        return light.uiColor
    }
}

总结

通过这种方式,可以统一swift颜色的设置。 优雅,永不过时