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

如何在SwiftUI View中修改非View结构体的Bool属性?

解决方案:实现CheckIn状态的跨View同步更新

你的核心问题是值类型数据的状态同步:CheckIn是结构体(值类型),直接传递给子View会生成副本,修改副本无法同步到原数据源。要实现点击按钮修改原数据并同步所有关联UI,需要结合ObservableObject(管理全局/父级数据源)和@Binding(传递单个元素的绑定引用)。


步骤1:用ObservableObject统一管理数据源

创建一个可观察的类来持有所有CheckIn数据,确保数据变化时自动通知所有依赖的View刷新:

import SwiftUI

class RosterManager: ObservableObject {
    // @Published标记数组,数据变化时自动触发View刷新
    @Published var checkInItems: [CheckIn]
    
    init() {
        // 加载原静态方法返回的数据
        self.checkInItems = RosterData.getRosterData()
    }
}

步骤2:在父View中初始化并绑定数据源

在你的RosterView中,用@StateObject创建管理器实例,并通过绑定遍历数组元素:

struct RosterView: View {
    // @StateObject用于持有ObservableObject实例,生命周期与View绑定
    @StateObject private var rosterManager = RosterManager()
    
    var body: some View {
        ScrollView {
            LazyVStack {
                Section {
                    // 用$符号获取数组元素的Binding,传递给子View
                    ForEach($rosterManager.checkInItems) { $checkIn in
                        CheckInListRow(checkIn: $checkIn)
                    }
                }
            }
        }
    }
}

步骤3:修改CheckInListRow使用@Binding接收数据

将子View的参数改为@Binding,直接绑定到原数据源的CheckIn实例,这样修改的就是原数据:

struct CheckInListRow: View {
    // @Binding绑定到原数据源的CheckIn,修改时同步更新原数据
    @Binding var checkIn: CheckIn
    
    var body: some View {
        HStack {
            Image(systemName: checkIn.isCheckedIn ? "clock.badge.checkmark" : "clock.badge.exclamationmark")
            VStack(alignment: .leading, spacing: 0) {
                Text("Check-in/Duty start")
                    .font(.footnote)
                Text(checkIn.checkInTime)
                    .font(.system(size: 16, weight: .bold, design: .rounded))
            }
            
            Spacer()
            
            if checkIn.checkInPlace == "ARN" && checkIn.checkInPossible {
                Button {
                    // 直接修改绑定的属性,原数据源同步更新,所有关联View自动刷新
                    checkIn.isCheckedIn = true
                } label: {
                    HStack {
                        Text(checkIn.isCheckedIn ? "You are checked in" : "Check in now")
                        Image(systemName: checkIn.isCheckedIn ? "checkmark.circle.fill" : "arrow.right.circle.fill")
                    }
                }
            } else {
                Text(checkIn.checkInPlace)
            }
        }
        .opacity(checkIn.isCheckedIn ? 0.4 : 1.0)
    }
}

#Preview {
    // 预览时用@State模拟绑定数据
    @State var sampleCheckIn = CheckIn(checkInTime: "07:00", checkInPlace: "ARN", checkInPossible: true, isCheckedIn: false)
    return CheckInListRow(checkIn: $sampleCheckIn)
}

跨View共享状态的补充方案

如果需要在多个不相关的View中访问签到状态(比如TabBar的不同页面),可以将RosterManager注入到环境中:

  1. 在App入口注入环境对象:
@main
struct CrewRosterApp: App {
    let rosterManager = RosterManager()
    
    var body: some Scene {
        WindowGroup {
            RosterView()
                .environmentObject(rosterManager) // 注入到全局环境
        }
    }
}
  1. 在任意View中通过@EnvironmentObject获取:
struct StatsView: View {
    @EnvironmentObject private var rosterManager: RosterManager
    
    var body: some View {
        Text("已签到人数:\(rosterManager.checkInItems.filter { $0.isCheckedIn }.count)")
    }
}

内容的提问来源于stack exchange,提问作者ScoobyDooku

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:44:51