如何使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
相关产品推荐
相关产品推荐

