You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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会占据父视图全部空间,但如果内部容器没有填充其空间,会压制子组件的交互区域。

修复步骤

  1. 调整GeometryReader结构:

    • 如果不需要依赖GeometryReader的尺寸信息,直接删除它,将VStack作为NavigationStack的直接子视图。
    • 如果必须保留GeometryReader,给内部的VStack添加.frame(maxWidth: .infinity, maxHeight: .infinity)修饰符,确保它填充GeometryReader的空间,同时保留交互能力。
  2. 验证绑定正确性:
    确认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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 18:24:51