简介
在 SwiftUI 中,App 使用变量 eventData
(事件数据)储存数据。eventData
通过@StateObject
(状态对象)属性包装器1进行定义,以创建可观测对象2 EventData
的实例。因为这个对象是可观测的,所以 SwiftUl 会监视它,来跟踪它的值的更改。每当数据发生变化,SwiftUl就会自动更新使用(或观测)该对象的视图。
数据传入
接着,代码会将 eventData
传入 EventList
(事件列表)视图。对于下面这段代码而言,EventList
是App启动时显示的第一个视图:
import SwiftUI
@main
struct DatePlannerApp: App {
@StateObject private var eventData = EventData()
var body: some Scene {
WindowGroup {
EventList(eventData: eventData)
// 将 eventData 传入 EventList。
}
}
}
可识别数据整理
为了整理数据,App会使用一系列 Event
对象,每个对象代表一个特定事件,如露营之旅或生日派对。为了符合 Identifiable
(可识别)协议,请确保在你创建事件列表时,SwiftUl 可以识别和更新每一行(如下图)。
![图片[1]-SwiftUI 可观测数据模型的使用-梦闯の天下](https://montrong-1300089193.cos.ap-beijing.myqcloud.com/montrong/2024/10/20241018112919853.png?imageMogr2/format/webp/interlace/1/quality/100)
Event 类型可包含信息
Event
类型包含填充事件需要的所有信息,包括符号、颜色、标题、日期和任务集。每个任务都是特定于事件的待办事项,如“准备一个露营灯”。(如下图)
![图片[2]-SwiftUI 可观测数据模型的使用-梦闯の天下](https://montrong-1300089193.cos.ap-beijing.myqcloud.com/montrong/2024/10/20241018113207807.png?imageMogr2/format/webp/interlace/1/quality/100)
import SwiftUI
struct Event: Identifiable, Hashable, Codable {
var 1d = UUID()
var symbol: String = EventSymbols. randomName ()
var color: RGBAColor = ColorOptions. random() .rgbaColor
var title = ""
var tasks = [EventTask(text: "") ]
var date = Date.now
// Other Code...
下面这是 Event
的计算属性列表。这些属性可帮助用户根据待完成任务的日期和数量整理列表小节。
var period: Period {
if date < Date.now{
return .past
} else if date < Date.now.sevenDaysOut {
return .nextSevenDays
} else if date < Date.now.thirtyDaysOut {
return .nextThirtyDays
} else {
return .future
}
}
var remainingTaskCount: Int {
tasks.filter { !$0.isCompleted && !$0.text.isEmpty }.count
}
var isComplete: Bool {
tasks.allSatisfy { $0.isCompleted || $0.text.isEmpty }
}
// Other Code...
EvenTask 标记
一个 EventTask
(事件任务)代表事件一系列待办事项中的一个。和 Event
一样,EventTask
也是 Identifiable
,可让 SwiftUl 管理和更新它在列表中的外观。
EventTask
包含文本和完成状态的属性,以及 isNew
(新的)属性。当用户将任务标记为已完成时,你需要将 isCompleted
(已完成)设为 true
,以允许 App 跟踪每个事件剩下的任务:
import Foundation
struct EventTask: Identifiable, Hashable, Codable {
var id = UUID()
var text: String
var isCompleted = false
var isNew = false
}
EventData
包含一个叫做 events
的属性,储存了Event
值的预填充数组,如游戏之夜或就诊预约。定义这个属性时,通过使用 @Published
(发布)属性包装器,可指示 SwiftUI 在events
数组发生变化时通知所有观测器,并更新视图。这样可让你在数组中添加和删除事件,以及立即在 UI 中查看更改。
import SwiftUI
class EventData: ObservableObject {
@Published var events: [Event] = [
Event(symbol: "gift.fill",
color: Color.red.rgbaColor,
title: "Maya's Birthday",
tasks: [EventTask(text: "Guava kombucha"),
EventTask(text: "Paper cups and plates"),
EventTask(text: "Cheese plate"),
EventTask(text: "Party poppers"),
],
// Other code...
将可观测数据传入
通过 @ObservedObject
属性包装器将 eventData
可观测对象传入 EventList
。这个属性包装器会在值发生任何变化时让 SwiftUI 更新视图。
import SwiftUI
struct EventList: View {
@ObservedObject var eventData: EventData //ObservedObject
@State private var isAddingNewEvent = false
@State private var newEvent = Event()
@State private var selection: Event?
var body: some View {
// Other code...
导航拆分视图
App 使用 NavigationSplitView
(导航拆分视图)在 App 内的不同视图间导航。在更宽的 App 配置中,如 iPad 上的全屏幕横屏模式,SwiftUI 会将 NavigationSplitView
内容显示为多个相邻的列,而不是单个堆叠。在这个布局中,EventList
显示在边栏列中,它的目的内容显示在主面板中。当有人在列表中选择了一个事件时,第二列会显示 EventEditor
(事件编辑器)视图;否则,它会显示占位符 Text
(文本)视图。(如下两图)若要创建列表,请创建 List
视图,并使用 ForEach
循环迭代所有时间段(nextSevenDays
, nextThirtyDays
, future
和 past
)。
当小节中包含事件时,请创建 Section
(小节)视图,并使用 ForEach
迭代该时间段中的所有事件。在EventData
中,使用 sortedEvents
(period:
)方法,以返回特定于小节时间范围的事件。在 ForEach
循环内,为时间段中的每个事件创建 EventRow
(事件行)视图。
若要删除事件,在事件行上添加修饰符 .swipeActions
(轻扫操作),并定义从 EventData
的 events
数组中移除事件的按钮。你现在可以在事件上向左轻扫,然后轻点“删除”按钮来移除当前事件。
![图片[3]-SwiftUI 可观测数据模型的使用-梦闯の天下](https://montrong-1300089193.cos.ap-beijing.myqcloud.com/montrong/2024/10/20241018115542738.png?imageMogr2/format/webp/interlace/1/quality/100)
![图片[4]-SwiftUI 可观测数据模型的使用-梦闯の天下](https://montrong-1300089193.cos.ap-beijing.myqcloud.com/montrong/2024/10/20241018115627986.png?imageMogr2/format/webp/interlace/1/quality/100)
图中完整示例代码如下:
import SwiftUI
struct EventList: View {
@ObservedObject var eventData: EventData
@State private var isAddingNewEvent = false
@State private var newEvent = Event()
@State private var selection: Event?
var body: some View {
NavigationSplitView {
List(selection: $selection) {
ForEach(Period.allCases) { period in
Section(content: {
ForEach(eventData.sortedEvents(period: period)) { $event in
EventRow(event: event)
.tag(event)
.swipeActions {
Button(role: .destructive) {
selection = nil
eventData.remove(event)
} label: {
Label("Delete", systemImage: "trash")
}
}
}
}, header: {
Text(period.name)
.font(.callout)
.foregroundColor(.secondary)
.fontWeight(.bold)
})
.disabled(eventData.sortedEvents(period: period).isEmpty)
}
}
.navigationTitle("Date Planner")
.toolbar {
ToolbarItem {
Button {
newEvent = Event()
isAddingNewEvent = true
} label: {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $isAddingNewEvent) {
NavigationStack {
EventEditor(event: $newEvent, isNew: true)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
isAddingNewEvent = false
}
}
ToolbarItem {
Button {
eventData.add(newEvent)
isAddingNewEvent = false
} label: {
Text("Add" )
}
.disabled(newEvent.title.isEmpty)
}
}
}
}
} detail: {
ZStack {
if let event = selection, let eventBinding = eventData.getBindingToEvent(event) {
EventEditor(event: eventBinding)
} else {
Text("Select an Event")
.foregroundStyle(.secondary)
}
}
}
}
}
struct EventList_Previews: PreviewProvider {
static var previews: some View {
EventList(eventData: EventData())
}
}
- 属性包装器是一种将常用行为模式应用到属性的简单方法。这个包装器是对属性的一种标注方式,定义了读取时它如何被储存或计算。
例如,CState
属性包装器告诉 SwiftUl 管理值的储存空间,并在值发生更改时,更新视图中任何使用了这个值的部分。 ↩︎ - 当类符合
ObservableObject
协议时,对它的发布值进行的任何更改都会导致使用这些值的所有视图自动更新,以反映变化。 ↩︎
若本站存在用户上传的侵权内容,请联系 Email,我们会处理相关内容和用户。
请登录后查看评论内容