SwiftUI深色模式下accent颜色编程获取异常问题求助
问题:编程获取Accent颜色时返回浅色模式值,单独使用却正常
当通过编程方式提取accent颜色的RGB分量时,它总是返回浅色模式下的颜色值,但直接使用Color.accent却能正确适配深浅模式,该怎么解决?

问题原因
问题出在getRGBA()方法里:直接把SwiftUI的Color转成UIColor时,没有传入当前视图环境的trait collection,导致UIColor默认只会解析浅色模式下的颜色值。而SwiftUI的Color本身是环境感知的,只有在视图的环境上下文里才能获取对应模式的正确颜色。
解决方案
方案1:传入当前环境的Trait Collection
修改颜色解析方法,让它接收当前模式的trait,确保获取对应模式的颜色值:
首先更新getRGBA()方法:
private func getRGBA(traitCollection: UITraitCollection) -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { let uiColor = UIColor(self) var red: CGFloat = 0 var green: CGFloat = 0 var blue: CGFloat = 0 var alpha: CGFloat = 0 // 传入当前环境的trait来获取对应模式的颜色 uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha, traitCollection: traitCollection) return (red, green, blue, alpha) }
然后更新colorRange静态方法,增加trait参数:
static func colorRange(from startColor: Color, to endColor: Color, count: Int, traitCollection: UITraitCollection) -> [Color] { guard count > 1 else { return [startColor] } let start = startColor.getRGBA(traitCollection: traitCollection) let end = endColor.getRGBA(traitCollection: traitCollection) let redDelta = end.red - start.red let greenDelta = end.green - start.green let blueDelta = end.blue - start.blue let alphaDelta = end.alpha - start.alpha return (0 ..< count).map { step in let progress = CGFloat(step) / CGFloat(count - 1) let red = start.red + redDelta * progress let green = start.green + greenDelta * progress let blue = start.blue + blueDelta * progress let alpha = start.alpha + alphaDelta * progress return Color(UIColor(red: red, green: green, blue: blue, alpha: alpha)) } }
最后在视图中监听颜色模式变化,动态生成颜色数组:
struct ColorRangeExamples: View { @Environment(\.colorScheme) private var colorScheme @State private var example1: [Color] = [] var body: some View { VStack(spacing: 30) { Color.accent.frame(height: 100).cornerRadius(10) HStack(spacing: 0) { ForEach(0 ..< example1.count, id: \.self) { index in example1[index] } } .frame(height: 50) .cornerRadius(10) } .padding() .onAppear { // 根据当前颜色Scheme生成对应的trait let trait = UITraitCollection(userInterfaceStyle: colorScheme == .dark ? .dark : .light) example1 = Color.colorRange(from: .accent, to: .secondary, count: 5, traitCollection: trait) } .onChange(of: colorScheme) { newScheme in // 模式切换时重新生成颜色数组 let trait = UITraitCollection(userInterfaceStyle: newScheme == .dark ? .dark : .light) example1 = Color.colorRange(from: .accent, to: .secondary, count: 5, traitCollection: trait) } } }
方案2:利用SwiftUI的环境解析功能
另一种方式是使用SwiftUI自带的Color.resolve(in:)方法,它会自动根据环境返回正确的UIColor:
修改getRGBA()方法:
private func getRGBA(in environment: EnvironmentValues) -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { // 利用环境解析出当前模式的正确颜色 let uiColor = self.resolve(in: environment) var red: CGFloat = 0 var green: CGFloat = 0 var blue: CGFloat = 0 var alpha: CGFloat = 0 uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) return (red, green, blue, alpha) }
更新colorRange方法:
static func colorRange(from startColor: Color, to endColor: Color, count: Int, in environment: EnvironmentValues) -> [Color] { guard count > 1 else { return [startColor] } let start = startColor.getRGBA(in: environment) let end = endColor.getRGBA(in: environment) let redDelta = end.red - start.red let greenDelta = end.green - start.green let blueDelta = end.blue - start.blue let alphaDelta = end.alpha - start.alpha return (0 ..< count).map { step in let progress = CGFloat(step) / CGFloat(count - 1) let red = start.red + redDelta * progress let green = start.green + greenDelta * progress let blue = start.blue + blueDelta * progress let alpha = start.alpha + alphaDelta * progress return Color(UIColor(red: red, green: green, blue: blue, alpha: alpha)) } }
修改视图代码:
struct ColorRangeExamples: View { @Environment(\.self) private var environment @State private var example1: [Color] = [] var body: some View { VStack(spacing: 30) { Color.accent.frame(height: 100).cornerRadius(10) HStack(spacing: 0) { ForEach(0 ..< example1.count, id: \.self) { index in example1[index] } } .frame(height: 50) .cornerRadius(10) } .padding() .onAppear { example1 = Color.colorRange(from: .accent, to: .secondary, count: 5, in: environment) } .onChange(of: environment.colorScheme) { _ in example1 = Color.colorRange(from: .accent, to: .secondary, count: 5, in: environment) } } }
两种方案都能让颜色数组跟随深浅模式动态更新,和直接使用Color.accent的表现一致。
内容的提问来源于stack exchange,提问作者Lê Công Minh Khôi
相关产品推荐
相关产品推荐

