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

SwiftUI macOS应用:子视图Toggle绑定ViewModel不生效求助

SwiftUI复选框状态无法同步回子视图问题修复

问题描述

主ContentView包含ChecklistView,该视图又包含多个内嵌Toggle组件的CheckView。点击复选框时,状态信息可传递至ContentViewModel,但无法同步回子视图。使用Xcode 16.3,Swift 5或Swift 6版本均存在该问题。

测试代码如下:

App入口代码

import SwiftUI
@main
struct TestCheckListApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView(cvm: ContentViewModel())
        }
    }
}

ChecklistItem模型

import SwiftUI
final class ChecklistItem: Identifiable, ObservableObject
{
    let id = UUID()
    var title: String
    var isChecked: Bool
    
    init(title: String, isChecked: Bool = false) {
        self.title = title
        self.isChecked = isChecked
    }
    func toggle() { isChecked.toggle() }
}

ContentViewModel

import SwiftUI
final class ContentViewModel: ObservableObject {
    @Published var report: String = ""
    @Published var arrItem: [ChecklistItem] = [
        ChecklistItem(title: "Item 0"),
        ChecklistItem(title: "Item 1"),
        ChecklistItem(title: "Item 2")
    ]
    func buttonTapped()
    {
        var s: String = ""
        for item in arrItem
        {
            s += "\(item.title) : \(item.isChecked), "
            print("\(item.title) : \(item.isChecked)")
        }
        report = s
    }
}

ContentView

import SwiftUI
struct ContentView: View {
    @ObservedObject var cvm: ContentViewModel
    @State private var multiSelection = Set<UUID>()
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Button(action: cvm.buttonTapped) {
                    Text("Display values in view model")
                }
                Text(cvm.report)
            }
            Divider()
            HStack {
                Text("Toggles in ContentView:")
                Toggle(cvm.arrItem[0].title, isOn: $cvm.arrItem[0].isChecked)
                Toggle(cvm.arrItem[1].title, isOn: $cvm.arrItem[1].isChecked)
                Toggle(cvm.arrItem[2].title, isOn: $cvm.arrItem[2].isChecked)
            }
            Divider()
            HStack {
                Text("Toggles in CheckView in ContentView:")
                CheckView(cli: cvm.arrItem[0])
                CheckView(cli: cvm.arrItem[1])
                CheckView(cli: cvm.arrItem[2])
            }
            Divider()
            Text("Toggles in List in ContentView:")
            List {
                ForEach($cvm.arrItem) { item in
                    Toggle("item.title", isOn: item.isChecked)
                }
            }
            Divider()
            Text("Toggles in ChecklistView:")
            ChecklistView(cl: cvm.arrItem)
        }
        .padding()
        .preferredColorScheme(.light)
    }
}
#Preview {
    ContentView(cvm: ContentViewModel())
}

CheckView

import SwiftUI
struct CheckView: View {
    @ObservedObject var cli: ChecklistItem
    var body: some View {
        Toggle(cli.title, isOn: $cli.isChecked)
    }
}

#Preview {
    CheckView(cli: ContentViewModel().arrItem[0])
}

ChecklistView

import SwiftUI
struct ChecklistView : View {
    var cl: [ChecklistItem]
    var body: some View {
        List {
            ForEach(cl) { item in
                CheckView(cli: item)
            }
        }
    }
}
#Preview {
    ChecklistView(cl: ContentViewModel().arrItem)
}

修复方案

1. 为ChecklistItem的属性添加@Published修饰

核心问题在于ChecklistItem的属性变化没有发送通知。作为ObservableObject,需要用@Published标记需要监听的属性,这样属性修改时会自动触发视图更新:

final class ChecklistItem: Identifiable, ObservableObject
{
    let id = UUID()
    @Published var title: String
    @Published var isChecked: Bool
    
    init(title: String, isChecked: Bool = false) {
        self.title = title
        self.isChecked = isChecked
    }
    func toggle() { isChecked.toggle() }
}

2. 可选优化:使用绑定传递数组(增强数据流控制)

如果希望子视图能感知数组本身的变化(比如元素增删),可以将ChecklistView的参数改为绑定类型:

// 修改ChecklistView
struct ChecklistView : View {
    @Binding var cl: [ChecklistItem]
    var body: some View {
        List {
            ForEach($cl) { $item in
                CheckView(cli: item)
            }
        }
    }
}

// 在ContentView中调用时传递绑定
ChecklistView(cl: $cvm.arrItem)

这一步不是必须的,但能让数据流更清晰,确保数组的所有变化都能被子视图捕获。

修改完成后,所有Toggle的状态会在ViewModel和所有关联视图间实时同步,点击任意复选框,所有对应视图都会立即更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 08:49:55