第一部分
创建颜色主题
为了在应用程序中创建一致的外观,您将创建一个主题,其中包含两个对比的颜色属性。您将使用主颜色作为视图的背景颜色,使用强调颜色作为视图的文本颜色。
注意:
请确保您使用的是本页面顶部链接处提供的起始项目。下面的步骤使用该项目中定义的颜色。
步骤1
在项目导航器中,创建一个名为Models的新组。
步骤2
在Models组中,创建一个名为Theme.swift的新的Swift文件。
步骤3
导入SwiftUI框架而不是Foundation框架。
尽管在本节中您并没有创建一个视图,但您可以从SwiftUI框架中添加颜色属性。SwiftUI将颜色视为可以直接添加到视图层次结构中的视图实例。
步骤4
创建一个名为Theme的枚举,其原始值类型为String。
Swift会自动创建与每个添加到Theme中的case名称匹配的字符串。
注意事项
编译器会生成错误,因为枚举必须有case来声明原始类型。您将在下一步中修复此错误。
起始项目包括资源目录,并为每个颜色定义了RGBA值。
步骤5
为资产目录中的Themes文件夹中列出的每种颜色添加case。
资源目录中的命名颜色必须与代码中的引用匹配,因此请确保正确拼写每个case的名称。
步骤6
添加一个名为accentColor的Color属性,根据self的值返回.black或.white。
强调颜色为主题的主色提供了高对比度的补充,确保您的视图保持可访问性。
步骤7
添加一个名为mainColor的Color属性,使用枚举的rawValue创建一个颜色。
此属性从资源目录中初始化一个颜色。
Theme.swift
import SwiftUI
enum Theme: String {
case bubblegum
case buttercup
case indigo
case lavender
case magenta
case navy
case orange
case oxblood
case periwinkle
case poppy
case purple
case seafoam
case sky
case tan
case teal
case yellow
var accentColor: Color {
switch self {
case .bubblegum, .buttercup, .lavender, .orange, .periwinkle, .poppy, .seafoam, .sky, .tan, .teal, .yellow: return .black
case .indigo, .magenta, .navy, .oxblood, .purple: return .white
}
}
var mainColor: Color {
Color(rawValue)
}
}
Section 2
创建每日站会模型
DailyScrum模型将包含以下四个属性,均为简单值类型:title、attendees、lengthInMinutes和theme。因为DailyScrum主要用于携带值数据,所以您将它声明为结构体,使其成为值类型。
Step 1
在Models组中添加一个名为DailyScrum的新Swift文件。
Step 2
创建一个DailyScrum结构体,包含title、attendees、lengthInMinutes和theme属性。
Step 3
添加一个扩展,提供一些示例数据。
DailyScrum.swift
import Foundation
struct DailyScrum {
var title: String
var attendees: [String]
var lengthInMinutes: Int
var theme: Theme
}
extension DailyScrum {
static let sampleData: [DailyScrum] =
[
DailyScrum(title: "Design",
attendees: ["Cathy", "Daisy", "Simon", "Jonathan"],
lengthInMinutes: 10,
theme: .yellow),
DailyScrum(title: "App Dev",
attendees: ["Katie", "Gray", "Euna", "Luis", "Darla"],
lengthInMinutes: 5,
theme: .orange),
DailyScrum(title: "Web Dev",
attendees: ["Chella", "Chris", "Christina", "Eden", "Karla", "Lindsey", "Aga", "Chad", "Jenn", "Sarah"],
lengthInMinutes: 5,
theme: .poppy)
]
}
Section 3
创建卡片视图
CardView将总结DailyScrum模型的数据,并显示标题、参与人数和持续时间。您将从较小的视图中组合CardView,每个视图显示DailyScrum结构中的一部分数据。
您将更新CardView_Previews结构,以便在开发视图时获得即时的可视反馈。
Step 1
添加一个名为CardView的新的SwiftUI视图文件。
如果预览被暂停了,您可以恢复它,以观察您在代码中进行的更改在预览中的反映。
Step 2
添加一个类型为DailyScrum的常量scrum。
注意
编译器会生成一个错误,因为CardView()初始化程序现在需要一个DailyScrum参数。您将在下一步中解决此问题。
Step 3
在CardView_Previews中,创建一个名为scrum的静态变量,并将其传递给CardView的初始化程序。
Step 4
使用scrum.theme.mainColor为预览指定一个背景颜色。
Step 5
使用previewLayout修饰符将预览的宽度设置为400,高度设置为60。
设置预览布局和背景颜色将CardView呈现为它将在List视图中显示的样子。在下一个教程中,您将将该视图嵌入到列表中。
Step 6
将画布设置从Live更改为Selectable。
Step 7
更新Text视图以显示scrum的标题,并将字体样式设置为headline。
Step 8
将Text视图嵌入到具有leading对齐的VStack中。
VStack在垂直线上排列子视图,并使用对齐参数将视图沿水平轴的位置。
Step 9
在Spacer后面添加一个HStack,其中嵌套了一个Label用于显示与会人数。
注意
Label和Image对于SF Symbol使用稍微不同的参数标签。在使用堆栈排列视图中创建的Image使用的是Image(systemName:),而不是Label(:systemImage:)。
Step 10
在HStack中添加另一个Label,并将其系统图像设置为“clock”。
Step 11
修改标签以显示会议持续时间。
您可以使用属性检查器自定义视图,代码会相应更新。或者您可以直接在代码中更改属性。在两种情况下,预览都会更新以反映您的更改。
Step 12
在标签之间添加一个Spacer,以显示围绕视图的信息。
CardView.swift
import SwiftUI
struct CardView: View {
let scrum: DailyScrum
var body: some View {
VStack(alignment: .leading) {
Text(scrum.title)
.font(.headline)
Spacer()
HStack {
Label("\(scrum.attendees.count)", systemImage: "person.3")
Label("\(scrum.lengthInMinutes)", systemImage: "clock")
}
}
}
}
struct CardView_Previews: PreviewProvider {
static var scrum = DailyScrum.sampleData[0]
static var previews: some View {
CardView(scrum: scrum)
.background(scrum.theme.mainColor)
.previewLayout(.fixed(width: 400, height: 60))
}
}
第四节:美化卡片
卡片视图显示有关每日Scrum的信息。在这一部分中,您将为卡片视图添加样式,突出显示最重要的信息,并修改视觉组件,以确保在明亮和暗黑模式下文本和背景视图之间有足够的对比度。
Step 1
给会议时长标签添加20个点的尾部填充。
提示
您还可以在视图的.leading、.top和.bottom边缘添加填充。
Step 2
将HStack中的所有字体样式设置为标题。
在标签和图像中使用的SF Symbols会随着字体权重和大小的变化进行合理的缩放和对齐。
Step 3
给VStack添加填充,使所有内容从边缘处靠近。
Step 4
将Xcode预览模式从"Selectable"更改为"Color Scheme Variants",以在明亮和暗黑外观中呈现视图。
在暗黑模式中,默认的浅色文本与黄色背景之间的对比度不足。
Step 5
使用scrum.theme.accentColor设置foregroundColor。
在Theme.swift中定义的accentColor变量会根据明亮或暗黑主题返回黑色或白色。
CardView.swift
import SwiftUI
struct CardView: View {
let scrum: DailyScrum
var body: some View {
VStack(alignment: .leading) {
Text(scrum.title)
.font(.headline)
Spacer()
HStack {
Label("\(scrum.attendees.count)", systemImage: "person.3")
Spacer()
Label("\(scrum.lengthInMinutes)", systemImage: "clock")
.padding(.trailing, 20)
}
.font(.caption)
}
.padding()
.foregroundColor(scrum.theme.accentColor)
}
}
struct CardView_Previews: PreviewProvider {
static var scrum = DailyScrum.sampleData[0]
static var previews: some View {
CardView(scrum: scrum)
.background(scrum.theme.mainColor)
.previewLayout(.fixed(width: 400, height: 60))
}
}
Section 5
自定义标签样式
接下来,您将创建一个标签样式,将会议时长和时钟图标水平堆叠在一起。通过使用LabelStyle协议,您可以通过在多个视图中重用相同的标签样式来创建一致的设计。
Step 1
创建一个名为TrailingIconLabelStyle.swift的新的Swift文件。
Step 2
导入SwiftUI。
Step 3
创建一个名为TrailingIconLabelStyle的新结构体,并符合LabelStyle协议。
提示
如果您不想创建自定义的标签样式,您可以使用内置的标签样式之一,它们使用系统标准布局显示图标、标题或两者。
注意
编译器会生成错误,因为该结构体不符合LabelStyle协议的要求。
Step 4
创建一个空的makeBody(configuration:)函数。
系统会在使用此样式作为当前标签样式的视图层次结构中的每个Label实例中调用此方法。
注意
现在您的项目可以编译通过,没有错误了。
Step 5
添加一个HStack。
Step 6
在HStack内部添加configuration.title和configuration.icon。
configuration参数是一个LabelStyleConfiguration,其中包含图标和标题视图。这些视图表示标签的图像和标签文本。
Step 7
在LabelStyle上添加一个扩展,创建一个名为trailingIcon的静态属性。
因为您将标签样式定义为静态属性,所以可以使用前导点语法来调用它,这样可以使您的代码更易读。
Step 8
在CardView.swift文件中的lengthInMinutes标签上,用新的标签样式替换padding。
现在,时钟图标对齐到尾部,与参与人数标签对齐。
在视觉上,CardView已经完成,您可以在列表中显示它。在这之前,您将使用无障碍修饰符来改善VoiceOver用户的体验。
TrailingIconLabelStyle.swift
import SwiftUI
struct TrailingIconLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.title
configuration.icon
}
}
}
CardView.swift
import SwiftUI
struct CardView: View {
let scrum: DailyScrum
var body: some View {
VStack(alignment: .leading) {
Text(scrum.title)
.font(.headline)
Spacer()
HStack {
Label("\(scrum.attendees.count)", systemImage: "person.3")
Spacer()
Label("\(scrum.lengthInMinutes)", systemImage: "clock")
.labelStyle(.trailingIcon)
}
.font(.caption)
}
.padding()
.foregroundColor(scrum.theme.accentColor)
}
}
struct CardView_Previews: PreviewProvider {
static var scrum = DailyScrum.sampleData[0]
static var previews: some View {
CardView(scrum: scrum)
.background(scrum.theme.mainColor)
.previewLayout(.fixed(width: 400, height: 60))
}
}
Section 6
使卡片视图具有可访问性
现在CardView在视觉上已经布局完成,您将使其对所有用户具有可访问性。您将为您的视图添加无障碍修饰符,以使VoiceOver更容易理解和导航您的界面。
CardView包含一个带有时钟图像和表示会议时长的数字的标签。它还包含一个人物图像和与会人数。
您将添加使用字符串插值的无障碍标签,以使标签成为视图的有意义描述。VoiceOver将读取“4名与会人员”和“10分钟会议”。
Step 1
在显示scrum标题的文本视图上应用accessibilityAddTraits(.isHeader)修饰符。
此修饰符通过读取scrum标题后跟“标题”来帮助传达视图的信息架构。
Step 2
使用accessibility修饰符为HStack中的第一个Label添加描述其内容的标签。
Step 3
为HStack中的第二个Label添加类似的accessibility修饰符。
CardView.swift
import SwiftUI
struct CardView: View {
let scrum: DailyScrum
var body: some View {
VStack(alignment: .leading) {
Text(scrum.title)
.font(.headline)
.accessibilityAddTraits(.isHeader)
Spacer()
HStack {
Label("\(scrum.attendees.count)", systemImage: "person.3")
.accessibilityLabel("\(scrum.attendees.count) attendees")
Spacer()
Label("\(scrum.lengthInMinutes)", systemImage: "clock")
.accessibilityLabel("\(scrum.lengthInMinutes) minute meeting")
.labelStyle(.trailingIcon)
}
.font(.caption)
}
.padding()
.foregroundColor(scrum.theme.accentColor)
}
}
struct CardView_Previews: PreviewProvider {
static var scrum = DailyScrum.sampleData[0]
static var previews: some View {
CardView(scrum: scrum)
.background(scrum.theme.mainColor)
.previewLayout(.fixed(width: 400, height: 60))
}
}
下一个教程将向您展示如何在SwiftUI List中使用CardView。您将了解到SwiftUI如何通过将较小的视图组合在一起来轻松构建一个大而复杂的视图,就像您刚刚创建的视图一样。