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

如何使SwiftUI的.sheet(item:)与.sheet(isPresented:)表现一致?

解决.sheet(item:)修改父视图状态导致Sheet反复弹出的问题

使用.sheet(item:)展示Sheet时,修改父视图的状态(比如示例里的things数组)会导致Sheet先关闭再重新弹出,但用.sheet(isPresented:)就不会有这个问题。需要用.sheet(item:)传递参数,同时让它的表现和.sheet(isPresented:)一致。

问题原因

你当前的ActiveSheet结构体里,id是计算属性,每次访问都会生成新的UUID。当父视图的things变化时,视图会重新渲染,activeSheet的id会被重新计算,SwiftUI会认为这是一个全新的Identifiable对象,所以会先关闭之前的Sheet,再展示新的。

解决方案

  • 把ActiveSheet的id改成存储属性,在初始化时生成一次,保证整个生命周期内id不变。
  • 如果需要传递参数,直接把参数放到ActiveSheet结构体中,这样既能通过item传递数据,又不会因为视图刷新导致id变化。

修改后的代码示例

import SwiftUI

struct SheetContentView: View {
    @Binding var things: [String]
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        NavigationStack {
            List {
                Button("关闭Sheet") { dismiss() }
                Button("添加条目") { things.append("\(things.count)") }
            }
        }
    }
}

struct ThingListContentView: View {
    @State private var activeSheet: ActiveSheet?
    @State private var things: [String] = []
    
    // 修改ActiveSheet:id改为存储属性,初始化时生成
    struct ActiveSheet: Identifiable {
        let id: String = UUID().uuidString
        // 可添加需要传递给Sheet的参数
        // 例如:let extraData: String
    }
    
    var body: some View {
        NavigationStack {
            Group {
                List(things, id: \.self) { thing in
                    Text(thing)
                }
                Button("弹出Sheet") {
                    // 若需传参,初始化时传入:
                    // activeSheet = ActiveSheet(extraData: "测试数据")
                    activeSheet = ActiveSheet()
                }
            }
            .navigationTitle("条目列表1")
            .sheet(item: $activeSheet, onDismiss: {
                print("Sheet关闭,当前条目数:\(things.count)")
            }) { sheet in
                SheetContentView(things: $things)
                // 若有参数,传递给子视图:
                // SheetContentView(things: $things, extraData: sheet.extraData)
            }
        }
    }
}

struct ThingList2ContentView: View {
    @State private var isPresented: Bool = false
    @State private var things: [String] = []
    
    var body: some View {
        NavigationStack {
            Group {
                List(things, id: \.self) { thing in
                    Text(thing)
                }
                Button("弹出Sheet") { isPresented = true }
            }
            .navigationTitle("条目列表2")
            .sheet(isPresented: $isPresented, onDismiss: {
                print("Sheet关闭,当前条目数:\(things.count)")
            }) {
                SheetContentView(things: $things)
            }
        }
    }
}

@main
struct TestSheetApp: App {
    var body: some Scene {
        WindowGroup {
            TabView {
                ThingListContentView()
                    .tabItem { Text("列表1") }
                ThingList2ContentView()
                    .tabItem { Text("列表2") }
            }
        }
    }
}

说明

修改后,ActiveSheet的id只会在初始化时生成一次,即使父视图的things变化导致视图刷新,activeSheet的id也不会改变,SwiftUI就不会认为是新的item,Sheet也就不会反复关闭弹出了。如果需要传递参数,直接在ActiveSheet里添加对应的属性,初始化时赋值,在Sheet的闭包里取出使用即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:07:20