使用@State仍无法更新SwiftUI视图?TextField与Picker无响应
问题描述
我开发了一个包含多个TextField、Picker组件的EventDetailsView,此前运行状态良好,但目前出现异常:视图无法响应任何修改操作,仅删除功能可正常使用,TextField和Picker均失效。若需要查看其他文件代码,请告知。
EventDetailsView 代码
import SwiftUI struct EventDetailsView: View { @EnvironmentObject var calendarViewModel: CalendarViewModel @Environment(\.dismiss) var dismiss @State private var event: Event // 传入当前编辑的事件 var body: some View { NavigationStack { GeometryReader { geo in VStack { // 所有Picker和TextField都在这里,绑定到event,我没展示它们,因为肯定不是这部分的问题 } Button("Delete Event", role: .destructive) { calendarViewModel.showingDeleteEventDialog = true } } .navigationTitle("Event Details") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button { dismiss() } label: { Label("Back", systemImage: "chevron.left") .labelStyle(.titleAndIcon) } } ToolbarItem(placement: .navigationBarTrailing) { Button("Save") { calendarViewModel.changeEvent(event) dismiss() } } } .alert("Failed to set a Notification.", isPresented: $calendarViewModel.showingAlertErrorAlert) { Button("Cancel", role: .cancel) { event.alert = .noAlerts calendarViewModel.setNoAlerts() } Button("Settings") { event.alert = .noAlerts calendarViewModel.setNoAlerts() calendarViewModel.openSettings() } } message: { Text("Please, enable notifications for this app in settings.") } .confirmationDialog("", isPresented: $calendarViewModel.showingDeleteEventDialog) { Button("Delete", role: .destructive) { calendarViewModel.deleteEvent(event) } Button("Cancel", role: .cancel) { } } message: { Text("Are you sure you want to delete this event?") } } } } init(event: Event) { _event = State(wrappedValue: event) // 传入事件并创建State对象 } } struct EventDetailsView_Previews: PreviewProvider { static var previews: some View { EventDetailsView(event: Event.example) .environmentObject(CalendarViewModel()) } }
Event 结构体代码
struct Event: Identifiable, Codable, Hashable, Comparable { var id = UUID() var title: String var description: String var tag: EventTags var startDate: Date var endDate: Date var repeatEvent: RepeatEvents var alert: Alerts var isCritical: Bool var sound: String var soundVolume: Double var color: EventColors.RawValue var background: Backgrounds var backgroundLocations: BackgroundLocation static let example = Event(title: "Workout", description: "We need to get strong", tag: .workout, startDate: Date(), endDate: Date().addingTimeInterval(86400), repeatEvent: .everyDay, alert: .noAlerts, isCritical: false, sound: "", soundVolume: 0.5, color: "darkGray", background: .color, backgroundLocations: BackgroundLocation.example) var stringStartDate: String { let formatter = DateFormatter() formatter.dateFormat = "MMM d, HH:mm a" return formatter.string(from: startDate) } var stringEndDate: String { let formatter = DateFormatter() formatter.dateFormat = "MMM d, HH:mm a" return formatter.string(from: endDate) } var stringNotificationTime: String { var formattedStart = "" var formattedEnd = "" if Calendar.current.isDate(startDate, inSameDayAs: endDate) { formattedStart = startDate.formatted(date: .omitted, time: .shortened) formattedEnd = endDate.formatted(date: .omitted, time: .shortened) } else { let formatter = DateFormatter() formatter.dateFormat = "MMM d, h:mm a" formattedStart = formatter.string(from: startDate) formattedEnd = formatter.string(from: endDate) } return "\(formattedStart) - \(formattedEnd)" } var currentTime: Double { let hour = Double(Calendar.current.component(.hour, from: startDate)) let time = hour / 24 return time } // 排序逻辑 static func <(lhs: Event, rhs: Event) -> Bool { lhs.startDate < rhs.startDate } static func == (lhs: Event, rhs: Event) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } // 此处省略其他枚举定义 }
跳转逻辑代码
.fullScreenCover(item: $calendarViewModel.selectedEvent) { event in EventDetailsView(event: event) }
问题原因及解决方案
核心问题:GeometryReader布局嵌套导致交互失效
你的EventDetailsView中,VStack直接嵌套在GeometryReader内部,但未正确设置布局约束,导致VStack内容无法获取交互焦点。GeometryReader会占据父视图全部空间,但如果内部容器没有填充其空间,会压制子组件的交互区域。
修复步骤
调整
GeometryReader结构:- 如果不需要依赖
GeometryReader的尺寸信息,直接删除它,将VStack作为NavigationStack的直接子视图。 - 如果必须保留
GeometryReader,给内部的VStack添加.frame(maxWidth: .infinity, maxHeight: .infinity)修饰符,确保它填充GeometryReader的空间,同时保留交互能力。
- 如果不需要依赖
验证绑定正确性:
确认TextField和Picker的绑定语法正确,例如TextField("Title", text: $event.title),确保绑定到@State变量event的属性。
修复后的核心代码示例
var body: some View { NavigationStack { // 移除不必要的GeometryReader,或保留并添加frame修饰符 VStack { // 示例TextField和Picker,确保绑定正确 TextField("事件标题", text: $event.title) .padding() Picker("标签", selection: $event.tag) { ForEach(EventTags.allCases, id: \.self) { tag in Text(tag.rawValue).tag(tag) } } .pickerStyle(.menu) Button("Delete Event", role: .destructive) { calendarViewModel.showingDeleteEventDialog = true } } .navigationTitle("Event Details") .navigationBarTitleDisplayMode(.inline) // 剩余toolbar、alert等代码保持不变 } }
额外检查点
- 确认
CalendarViewModel中的selectedEvent是@Published的Event?类型,确保fullScreenCover能正确触发视图展示。 - 再次确认
Event结构体的所有属性均为可变(var修饰),你的代码已满足此要求。
内容的提问来源于stack exchange,提问作者Nikita Kolomoec
相关产品推荐
相关产品推荐

