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

SwiftUI使用LazyVGrid时点击网格项仅打开数组首个元素DetailView

问题根因

你把sheet(isPresented:)修饰符放在了ForEach循环内部,所有网格项共用同一个@State var detailView = false布尔值控制弹窗状态。当任意按钮把detailView设为true时,SwiftUI会触发最先渲染完成的第一个网格项绑定的弹窗,因此永远加载数组第一个元素的详情。之前使用List时表现正常是List的懒加载渲染逻辑的巧合,本质写法不符合SwiftUI的弹窗规范,本身就存在隐患。

修复方案

推荐使用sheet(item:)重载实现多数据项的弹窗绑定,这是SwiftUI专门针对这类场景提供的原生方案:

  1. 替换原有的弹窗控制状态变量
    把@State var detailView = false修改为@State var selectedItem: Item?
  2. 修改按钮点击逻辑
    把按钮的action改为self.selectedItem = item
  3. 把sheet修饰符从ForEach循环中移出,放到外层视图上,使用item参数绑定选中项
  4. 额外优化:ListRowView和DetailView中的item不需要用@State修饰,改为普通let常量即可,@State仅用于管理视图自身的可变私有状态,不需要修改的传值属性直接用常量声明即可。
修复后的核心代码示例
struct ListView: View {
    @EnvironmentObject var listViewModel: ListViewModel
    @State var addItemView = false
    // 替换原有的detailView布尔值
    @State var selectedItem: Item?
    
    let columns = [GridItem(.flexible()), GridItem(.flexible())]

    var body: some View {
        NavigationView {
            ZStack {
                Color(.white)
                VStack {
                    Button(action: {
                        self.addItemView = true
                    }, label: {
                        Text("Add")
                    }).sheet(isPresented: $addItemView, content: {
                        AddItemView()
                    })
                    LazyVGrid(columns: columns, spacing: 20) {
                        ForEach(listViewModel.item, id:\.id){ item in
                            Button(action: {
                                // 点击时给选中项赋值
                                self.selectedItem = item
                            }, label: {
                                ListRowView(item: item)
                            })
                            // 移除循环内部的sheet修饰符
                        }
                    }
                    // sheet移到外层,绑定选中项
                    .sheet(item: $selectedItem, content: { item in
                        DetailView(item: item)
                    })
                    Spacer()
                }
            }
        }
    }
}

// 优化ListRowView的属性声明
struct ListRowView: View {
    // 去掉@State,改为let常量
    let item: Item

    var body: some View{
        VStack {
            Text(item.name).foregroundColor(.white)
        }.frame(width: 150, height: 150, alignment: .center)
        .background(Color.blue)
        .cornerRadius(10)
    }
}

// 优化DetailView的属性声明
struct DetailView: View {
    // 去掉@State,改为let常量
    let item: Item

    var body: some View {
        Text(item.name)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 10:39:03