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

SwiftUI多列表Sheet快速Dismiss/Present异常问题求助

SwiftUI Sheet弹窗异常问题分析与疑问

场景与问题

正在构建类似Netflix iOS应用首页的多行单元格视图,结构如下:

  • CollectionView():展示单个项目(以矩形形式呈现)
  • ListView():包含水平滚动的CollectionView项目列表
  • ContentView():通过多个ListView实例构建垂直多行布局
  • 点击每个单元格时,通过.sheet(isPresented:Content:)弹出对应详情视图

遇到的异常:快速点击第一个Collection的单元格弹出详情,下拉关闭后立即点击第二个Collection的项目,详情视图无法弹出;持续点击不同项目后,某一个详情视图弹出,关闭后之前所有尝试点击的详情会依次弹出。该问题偶尔在非快速点击场景下也会出现。

初始调试代码

CollectionModel 定义

struct CollectionModel: Identifiable {
    let id: UUID = UUID()
    let randomNumb: Int = Int.random(in: 1...10)
    
    static var mockArray: [CollectionModel] {
        [
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel(),
            CollectionModel()
        ]
    }
}

主视图 ContentView

struct ContentView: View {
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            LazyVStack {
                ListView()
                ListView()
                ListView()
                ListView()
            }
        }.padding(.horizontal, 2)
    }
}

水平列表视图 ListView

struct ListView: View {
    var listOfCollectionModel: [CollectionModel] = CollectionModel.mockArray
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack{
                ForEach(listOfCollectionModel) { model in
                    CollectionView(collectionModel: model)
                }
            }.padding(.horizontal)
        }
    }
}

单个项目视图 CollectionView

struct CollectionView: View {
    @State var collectionModel: CollectionModel
    @State var showDetails: Bool = false
    
    var body: some View {
        Text("\(collectionModel.randomNumb)")
            .padding()
            .frame(width: 200, height: 250)
            .background(.red.gradient)
            .clipShape(RoundedRectangle(cornerRadius: 15))
            .font(.title)
            .bold()
            .foregroundStyle(.white)
            .onTapGesture {
                showDetails = true
            }
            .sheet(isPresented: $showDetails){
                ZStack{
                    Color.red
                        .ignoresSafeArea()
                    Text("\(collectionModel.randomNumb)")
                }
            }
    }
}

尝试的解决方案

改用.sheet(item:)方案

学习相关教程后,将Sheet的触发方式改为.sheet(item:),调整代码如下:

修改后的 ListView

struct ListView: View {
    var listOfCollectionModel: [CollectionModel] = CollectionModel.mockArray
    @State var selectedModel: CollectionModel? = nil
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack{
                ForEach(listOfCollectionModel) { model in
                    CollectionView(collectionModel: model)
                        .onTapGesture {
                            selectedModel = model
                        }
                }
            }.padding(.horizontal)
                .sheet(item: $selectedModel) { model in
                    ZStack {
                        Color.black
                            .ignoresSafeArea()
                        Text("\(model.randomNumb)")
                            .font(.title)
                            .bold()
                            .foregroundStyle(.white)
                    }
                }
        }
    }
}

修改后的 CollectionView

struct CollectionView: View {
    @State var collectionModel: CollectionModel
    
    var body: some View {
        Text("\(collectionModel.randomNumb)")
            .padding()
            .frame(width: 200, height: 250)
            .background(.red.gradient)
            .clipShape(RoundedRectangle(cornerRadius: 15))
            .font(.title)
            .bold()
            .foregroundStyle(.white)
    }
}

此时弹窗能正常弹出,但偶尔会显示之前选中项目的内容。

添加.id(sheetID)修复

为解决内容显示异常的问题,添加了sheetID来强制刷新Sheet视图:

最终修改的 ListView

struct ListView: View {
    var listOfCollectionModel: [CollectionModel] = CollectionModel.mockArray
    @State var selectedModel: CollectionModel? = nil
    @State var sheetID: UUID = UUID()
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            LazyHStack{
                ForEach(listOfCollectionModel) { model in
                    CollectionView(collectionModel: model)
                        .onTapGesture {
                            selectedModel = model
                            sheetID = UUID()
                        }
                }
            }.padding(.horizontal)
                .sheet(item: $selectedModel) { model in
                    ZStack {
                        Color.black
                            .ignoresSafeArea()
                        Text("\(model.randomNumb)")
                            .font(.title)
                            .bold()
                            .foregroundStyle(.white)
                    }.id(sheetID)
                }
        }
    }
}

疑问

目前修改后弹窗看似能正常工作,但感觉这只是通过叠加修复掩盖了初始实现的问题,希望了解问题的本质根源,以及更合理的解决方案。@Benzy Neez的回复有效,但仍想明确问题根源。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:24:56