颜色作为一个App重要的组成,需要被认真地对待。在App开发中,通常情况下,我们需要用到颜色的地方,比如背景色,比如字体颜色。
在SwiftUI中,颜色是Color。它既可以很简单,也可以很复杂。比如给一个视觉元素设置背景色为白色,可以:
swift
VStack {
Text("Hello World")
}
.background(.white)
看似很简单,然而需要考虑的东西还不少。比如Dark Mode下,这个页面看起来应该是如何的。 因为Text没有设置字体颜色,所以使用的是系统默认的颜色。这颜色在Light Mode下是黑色,而在Dark Mode下则是白色。 因此,上面的页面,在Dark Mode下就看不到文字了。(我就吃过这个亏,上架审核的时候,说我的App在Dark Mode下不能正常使用。) 那要如何改呢? 这里要用到colorScheme。这个是一个环境变量,表示当前页面是Light Mode还是Dark Mode。 比如上面的代码就可以改成:
swift
VStack {
Text("Hello World")
}
.background(colorScheme == .light ? .white : .black)
这样的做法固然没错,也很直接。但试想一下,如果一个App里每个涉及到Color的地方,都要进行这样的判断,那代码简直不忍直视。而且如果有地方忽略了,那就会给用户造成不好的体验。 UIColor可以使用public init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)
根据Light或者Dark创建实例,达到自动适应Dark Mode的问题。 SwiftUI中是否有这样的方法呢?其实是有的。比如Color.primary,就会根据colorScheme改变颜色。当Light Mode的时候,primary是黑色,而Dark Mode下,primary就是白色。 我们自己怎么实现这样的功能? 这需要分情况考虑。 在低于iOS 17的系统,没法直接用代码实现,需要在Assets.xcassets文件中创建color set来实现。 ![[Pasted image 20250423100215.png]] 使用的时候用名称就可以取得。let background = Color("background")
这里简单提一下,重点还是放在iOS 17之后的代码实现上。 iOS 17新增了ColorResource
,上面的代码可以写成let background = Color(.background)
,编译器会帮忙检测background是否存在。 不过iOS 17可以用纯代码实现相应的功能,纯代码的爱好者应该会更喜欢。
swift
public struct SchemeColor: Hashable, ShapeStyle {
public let any: Color.Resolved?
public let light: Color.Resolved
public let dark: Color.Resolved
public init(
any: Color.Resolved,
light: Color.Resolved,
dark: Color.Resolved
) {
self.any = any
self.light = light
self.dark = dark
}
public init(
light: Color.Resolved,
dark: Color.Resolved
) {
any = nil
self.light = light
self.dark = dark
}
public func resolve(in environment: EnvironmentValues) -> Color.Resolved {
switch environment.colorScheme {
case .light:
light
case .dark:
dark
@unknown default:
any ?? light
}
}
}
我们引入一个SchemeColor
类型,实现Hashable
和ShapeStyle
类型,resolve函数返回Color.Resolved
,这样,就可以用SchemeColor
创建Color
了。 用到的是Color
的
swift
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
public init<T>(_ color: T) where T : Hashable, T : ShapeStyle, T.Resolved == Color.Resolved
初始化方法。 这样上面的实现就可以变成下面这样:
swift
let background = Color(
SchemeColor(
light: .init(red: 1, green: 1, blue: 1),
dark: .init(red: 0, green: 0, blue: 0)
)
)
swift
VStack {
Text("Hello World")
}
.background(Color.background)
这样代码看起来就简洁多了,不用到处都是colorScheme == .light ? .white : .black
的判断。
Color包含的内容还有许多,这一篇没法完全容纳,如果想要了解更多,可以关注我下一篇文章。