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

SwiftUI中如何通过Picker值修改列表数据?

问题分析与解决方案

核心错误点

  1. 数据模型误用@State
    LevelData是值类型结构体,内部的@State var minutes/seconds完全错误。@State仅用于View内部的私有状态,值类型结构体中的@State无法被SwiftUI正确追踪,修改后不会触发视图更新,还会导致Picker绑定警告。

  2. Sheet绑定逻辑错误
    使用全局的showingSheet控制所有列表项的Sheet,点击不同项时,Sheet实际绑定的是循环中最后一次迭代的item副本,修改操作无法同步到原数组的元素中。

  3. Picker绑定无效
    ForEach循环中的item是不可变的let常量,直接绑定item.$minutes既会触发警告,又因为是值类型的副本,修改不会影响原数组数据。


修正步骤与完整代码

第一步:重构数据模型

将LevelData改为遵循ObservableObject的类,用@Published修饰需要响应式更新的属性:

import SwiftUI
import Combine

class LevelData: Identifiable, ObservableObject {
    let id = UUID()
    let imageName: String
    let title: String
    @Published var minutes: Int
    @Published var seconds: Int
    
    init(imageName: String, title: String, minutes: Int, seconds: Int) {
        self.imageName = imageName
        self.title = title
        self.minutes = minutes
        self.seconds = seconds
    }
}

第二步:调整View的状态管理

  • 移除全局的showingSheet,改用selectedItem记录当前要编辑的列表项
  • 用sheet(item:)绑定选中项,自动处理Sheet的显示/隐藏
  • 修正Picker的绑定,直接关联选中项的@Published属性

完整修正代码

struct IntervalListView: View {
    
    static let Level4Interval1Medium: [LevelData] = [
        LevelData(imageName: "BreatheSymbol", title: "Breathe", minutes: 2, seconds: 00),
        LevelData(imageName: "HoldsSymbol", title: "Hold", minutes: 2, seconds: 00),
        LevelData(imageName: "BreatheSymbol", title: "Breathe", minutes: 1, seconds: 45),
        LevelData(imageName: "HoldsSymbol", title: "Hold", minutes: 2, seconds: 00),
    ]
    
    @State var levelDatas: [LevelData] = Level4Interval1Medium
    @State private var selectedItem: LevelData?
    
    var body: some View {
        ZStack {
            VStack {
                List {
                    Section {
                        HStack {
                            Text("14 min")
                            Spacer()
                            NavigationLink("Start", destination: SomeView())
                                .frame(width: 60.0)
                                .foregroundColor(.blue)
                            Spacer()
                            Image("Save")
                        }
                        .listRowBackground(Color(.secondarySystemBackground))
                    }
                    
                    ForEach(levelDatas) { item in
                        HStack {
                            // 点击触发Sheet,传入当前选中项
                            Button(action: {
                                selectedItem = item
                            }) {
                                // 可替换为可见的按钮内容,比如编辑图标
                                Image(systemName: "pencil")
                            }
                            .sheet(item: $selectedItem) { _ in
                                ZStack {
                                    VStack(spacing: 20) {
                                        Text("Set \(item.title) Time")
                                            .bold()
                                            .padding()
                                            .frame(maxWidth: .infinity)
                                            .background(Color(red: 0, green: 0.22, blue: 0.36))
                                            .foregroundColor(.white)
                                        Spacer()
                                        HStack {
                                            VStack {
                                                Picker("", selection: $selectedItem!.minutes) {
                                                    ForEach(0..<9, id: \.self) { i in
                                                        Text("\(i) min").padding(.leading, 40.0).tag(i)
                                                    }
                                                }
                                                .pickerStyle(WheelPickerStyle())
                                                .clipped()
                                            }
                                            VStack {
                                                Picker("", selection: $selectedItem!.seconds) {
                                                    ForEach(0..<60, id: \.self) { i in
                                                        Text("\(i) sec").padding(.trailing, 40.0).tag(i)
                                                    }
                                                }
                                                .pickerStyle(WheelPickerStyle())
                                                .clipped()
                                            }
                                        }
                                        Spacer()
                                        Button(action: {
                                            selectedItem = nil
                                        }) {
                                            Text("Done")
                                        }
                                        .padding()
                                    }
                                }
                                .presentationDetents([.medium, .large])
                            }
                            
                            Image(item.imageName)
                            Text(item.title)
                                .font(.title2)
                                .fontWeight(.semibold)
                            Spacer()
                            Text("\(item.minutes):\(item.seconds < 10 ? "0" : "")\(item.seconds)")
                                .padding(.trailing, 10.0)
                        }
                    }
                    .onDelete { indexSet in
                        levelDatas.remove(atOffsets: indexSet)
                    }
                }
                .navigationTitle("Overview Intervals")
                .toolbar {
                    EditButton()
                }
            }
        }
    }
}

// 补充缺失的SomeView
struct SomeView: View {
    var body: some View {
        Text("Start View")
    }
}

struct IntervalListView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationStack {
            IntervalListView()
        }
    }
}

关键修改说明

  1. ObservableObject + @Published:将数据模型改为类并遵循ObservableObject,用@Published标记minutes和seconds,确保属性变化时自动通知视图更新。
  2. 选中项绑定Sheet:用selectedItem记录当前编辑的对象,通过sheet(item: $selectedItem)自动关联Sheet的显示,避免全局状态导致的冲突。
  3. 正确的Picker绑定:直接绑定$selectedItem!.minutes,因为selectedItem是ObservableObject实例,绑定其@Published属性会直接修改原数组中的元素,同步更新列表显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 14:40:34