在前一篇中,我們已經完成了「飲食紀錄清單頁」,使用者能夠查看已儲存的紀錄。
今天,我們要讓使用者點擊任一餐點後,進入該筆紀錄的「詳細資訊頁面」,並能進行「編輯」或「刪除」操作。
MealDetailView
)NavigationLink
從清單頁跳轉至詳細頁ViewModel 負責儲存目前查看的餐點,並提供編輯、刪除的功能。
import SwiftUI
import SwiftData
import Combine
class MealDetailViewModel: ObservableObject {
@Published var meal: MealRecord
@Published var showEditView = false
@Published var showDeleteAlert = false
@Published var deleteSuccess = false
private let repository = MealRepository.shared
init(meal: MealRecord, ) {
self.meal = meal
}
func onAppear(modelContext: ModelContext) {
Task {
await repository.configure(context: modelContext)
}
}
func deleteMeal() {
Task {
await repository.delete(meal)
}
deleteSuccess = true
}
func editMeal() {
showEditView = true
}
func dismissEditView() {
showEditView = false
}
}
接著,我們建立新的畫面來顯示該筆餐點的完整資料。
import SwiftUI
import SwiftData
struct MealDetailView: View {
@Environment(\.modelContext) private var modelContext
@Environment(\.dismiss) private var dismiss
@StateObject private var viewModel: MealDetailViewModel
init(meal: MealRecord) {
_viewModel = StateObject(wrappedValue: MealDetailViewModel(
meal: meal,
))
}
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(viewModel.meal.name)
.font(.largeTitle)
.bold()
Divider()
VStack(alignment: .leading, spacing: 8) {
Text("熱量:\(viewModel.meal.calories) kcal")
Text("餐別:\(viewModel.meal.category.rawValue)")
}
Spacer()
HStack {
Button("編輯") {
viewModel.editMeal()
}
.buttonStyle(.borderedProminent)
Button("刪除", role: .destructive) {
viewModel.showDeleteAlert = true
}
.buttonStyle(.bordered)
}
}
.padding()
.navigationTitle("詳細資訊")
.toolbar(.hidden, for: .tabBar)
.alert("確定要刪除此紀錄?", isPresented: $viewModel.showDeleteAlert) {
Button("刪除", role: .destructive) {
viewModel.deleteMeal()
}
Button("取消", role: .cancel) { }
}
.sheet(isPresented: $viewModel.showEditView) {
EditMealView(viewModel: viewModel)
}
.onAppear() {
viewModel.onAppear(modelContext: modelContext)
}
.onChange(of: viewModel.deleteSuccess) { _, success in
if success {
viewModel.deleteSuccess = false
dismiss()
}
}
}
}
#Preview {
MealDetailView(meal: MealRecord(name: "燕麥牛奶", calories: 200, category: .breakfast, date: Date()))
}
在編輯頁中,我們與 MealDetailView
共用相同的 ViewModel,直接修改同一筆餐點資料。
import SwiftUI
import SwiftData
struct EditMealView: View {
@ObservedObject var viewModel: MealDetailViewModel
@Environment(\.dismiss) private var dismiss
var body: some View {
Form {
// 餐點資訊
Section("餐點資訊") {
TextField("餐點名稱", text: $viewModel.meal.name)
// 卡路里
TextField("卡路里", value: $viewModel.meal.calories, format: .number)
.keyboardType(.numberPad)
// 餐別 Picker
Picker("餐別", selection: $viewModel.meal.category) {
ForEach(MealCategory.allCases, id: \.self) { category in
Text(category.rawValue)
}
}
}
// 儲存按鈕
Section {
Button("儲存") {
viewModel.showEditView = false
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
.navigationTitle("編輯餐點")
}
}
#Preview {
EditMealView(viewModel: MealDetailViewModel(meal: MealRecord(name: "燕麥牛奶", calories: 200, category: .breakfast, date: Date())))
}
最後,我們在清單畫面中加入 NavigationLink
,讓使用者可以點擊跳轉至詳細頁。
ForEach(viewModel.records) { meal in
NavigationLink(destination: MealDetailView(meal: meal)) {
VStack(alignment: .leading, spacing: 6) {
Text(meal.name)
.font(.headline)
Text("\(meal.calories) 大卡")
.foregroundColor(.gray)
Text(meal.category.rawValue)
.font(.caption)
.foregroundColor(.secondary)
}
.padding(.vertical, 4)
}
}
今天我們完成了飲食記錄 App 的詳細記錄頁