You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何在@AppStorage中存储Color类型值?

解决SwiftUI中@AppStorage存储Color类型的问题

@AppStorage默认仅支持存储遵循RawRepresentable协议的类型(如String、Int、Bool等),而SwiftUI的Color类型并未实现该协议,因此直接声明@AppStorage("selectedColor") var selectedColor: Color = .teal会报错。以下是两种可行的解决方法:

方案一:给Color扩展RawRepresentable协议

通过将Color转换为Data再编码成Base64字符串,实现RawRepresentable协议,让@AppStorage可以直接存储Color:

import SwiftUI
import UIKit

extension Color: RawRepresentable {
    public init?(rawValue: String) {
        guard let data = Data(base64Encoded: rawValue) else {
            self = .black
            return
        }
        do {
            let uiColor = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) ?? .black
            self = Color(uiColor)
        } catch {
            self = .black
        }
    }

    public var rawValue: String {
        do {
            let uiColor = UIColor(self)
            let data = try NSKeyedArchiver.archivedData(withRootObject: uiColor, requiringSecureCoding: false)
            return data.base64EncodedString()
        } catch {
            return ""
        }
    }
}

完成扩展后,你就可以直接使用最初的代码:

@AppStorage("selectedColor") var selectedColor: Color = .teal

方案二:通过Hex字符串间接存储

如果不想扩展Color类型,可以通过计算属性,将Color与Hex字符串互相转换,间接存储:

import SwiftUI
import UIKit

// 存储Hex字符串的@AppStorage变量
@AppStorage("selectedColorHex") private var selectedColorHex: String = "#34C759" // 默认对应.teal的Hex值

// 对外暴露的Color计算属性
var selectedColor: Color {
    get { Color(hex: selectedColorHex) }
    set { selectedColorHex = newValue.toHex() }
}

// 给Color添加Hex转换扩展
extension Color {
    /// 从Hex字符串初始化Color
    init(hex: String) {
        let cleanHex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var hexValue: UInt64 = 0
        Scanner(string: cleanHex).scanHexInt64(&hexValue)
        
        let a, r, g, b: UInt64
        switch cleanHex.count {
        case 3: // 12位RGB,如#RGB
            (a, r, g, b) = (255, (hexValue >> 8) * 17, (hexValue >> 4 & 0xF) * 17, (hexValue & 0xF) * 17)
        case 6: // 24位RGB,如#RRGGBB
            (a, r, g, b) = (255, hexValue >> 16, hexValue >> 8 & 0xFF, hexValue & 0xFF)
        case 8: // 32位ARGB,如#AARRGGBB
            (a, r, g, b) = (hexValue >> 24, hexValue >> 16 & 0xFF, hexValue >> 8 & 0xFF, hexValue & 0xFF)
        default:
            (a, r, g, b) = (255, 0, 0, 0)
        }
        
        self.init(
            .sRGB,
            red: Double(r) / 255,
            green: Double(g) / 255,
            blue: Double(b) / 255,
            opacity: Double(a) / 255
        )
    }
    
    /// 将Color转换为Hex字符串
    func toHex() -> String {
        let uiColor = UIColor(self)
        guard let components = uiColor.cgColor.components, components.count >= 3 else {
            return "#000000"
        }
        
        let r = Float(components[0])
        let g = Float(components[1])
        let b = Float(components[2])
        let a = Float(uiColor.cgColor.alpha)
        
        if a == 1.0 {
            return String(format: "#%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
        } else {
            return String(format: "#%02lX%02lX%02lX%02lX", lroundf(a * 255), lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
        }
    }
}

方案对比

  • 方案一:使用更便捷,和原生支持的@AppStorage类型用法一致,但依赖NSKeyedArchiver/Unarchiver,需注意跨平台兼容性(如macOS、watchOS)。
  • 方案二:存储的Hex字符串可读性高,跨平台表现更稳定,但需要额外处理Color与Hex的转换逻辑。

内容的提问来源于stack exchange,提问作者Saeed Noshadi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:25:22