SwiftUI的sheet在isPresented值从闭包中改变时无法关闭

5

我有一个表格视图,当用户点击下面父视图中的按钮时会呈现出来:

struct ViewWithSheet: View {
    @State var showingSheetView: Bool = false
    @EnvironmetObject var store: DataStore()

    var body: some View {
        NavigationView() {
            ZStack {
               Button(action: { self.showingSheetView = true }) {
                   Text("Show sheet view")
               }
            }
            .navigationBarHidden(true)
            .navigationBarTitle("")
            .sheet(isPresented: $showingSheetView) {
                SheetView(showingSheetView: self.$showingSheetView).environmentObject(self.dataStore)
            }
        }
    }
}

在表视图中,当用户点击另一个按钮时,存储器将执行一个带有完成处理程序的操作。完成处理程序返回一个对象值,如果该值存在,则应该关闭SheetView。
struct SheetView: View {

    @Binding var showingSheetView: Bool

    @EnvironmentObject var store: DataStore()

    //@Environment(\.presentationMode) private var presentationMode

    func create() {
        store.createObject() { object, error in 
           if let _ = object {
               self.showingSheetView = false
               // self.presentationMode.wrappedValue.dismiss()
           }
        }
    }

    var body: some View {
        VStack {
            VStack {
                HStack {
                    Button(action: { self.showingSheetView = false }) {
                        Text("Cancel")
                    }
                    
                    Spacer()
                    
                    Spacer()
                    
                    Button(action: { self.create() }) {
                        Text("Add")
                    }
                }
                .padding()
            }
        }
    }
}

然而,在create()函数中,一旦store返回值并且showingSheetView设置为false,该表视图不会按预期解除显示。我尝试过使用presentationMode解除显示表视图,但这也似乎无效。
3个回答

2

我找到了问题所在,由于我的整个应用程序包装视图中的条件限制,该表格没有被解除。我有一个if语句,在应用程序启动时会显示加载视图,但是在我的DataStore中,我在每个函数调用中都设置它的获取变量。当该值发生变化时,我的表格视图后面的视图堆栈将重新呈现LoadingView,然后再次更改获取变量时呈现TabView。这使得表格视图无法解除。以下是我的AppView的示例:

struct AppView: View {

    @State private var fetchMessage: String = ""

    @EnvironmentObject var store: DataStore()

    func initializeApp() {
        self.fetchMessage = "Getting App Data"
        store.getData() { object, error in 
            if let error = error {
                self.fetchMessage = error.localizedDescription
            }

            self.fetchMessage = ""
        }
    }

    var body: some View {
        Group {
            ZStack {
                //this is where my issue was occurring
                if(!store.fetching) {
                    TabView {
                        Tab1().tabItem {
                            Image(systemName: "tab-1")
                            Text("Tab1")
                        }

                        Tab2().tabItem {
                            Image(systemName: "tab-2")
                            Text("Tab2")
                        }

                        //Tab 3 contained my ViewWithSheet() and SheetView()
                        Tab3().tabItem {
                            Image(systemName: "tab-3")
                            Text("Tab3")
                        }
                    }
                } else {
                    LoadingView(loadingMessage: $fetchMessage)
                }
            }
        }.onAppear(perform: initializeApp)
    }
}

为了解决我的问题,我在我的 DataStore 中添加了另一个名为 initializing 的变量,我用它来在我的应用程序的第一个 .onAppear 事件中呈现加载屏幕或实际应用视图。以下是我更新后的 AppView 的示例:
struct AppView: View {

    @State private var fetchMessage: String = ""

    @EnvironmentObject var store: DataStore()

    func initializeApp() {
        self.fetchMessage = "Getting App Data"
        store.getData() { object, error in 
            if let error = error {
                self.fetchMessage = error.localizedDescription
            }

            self.fetchMessage = ""
            //set the value to false once I'm done getting my app's initial data.
            self.store.initializing = false
        }
    }

    var body: some View {
        Group {
            ZStack {
                //now using initializing instead
                if(!store.initializing) {
                    TabView {
                        Tab1().tabItem {
                            Image(systemName: "tab-1")
                            Text("Tab1")
                        }

                        Tab2().tabItem {
                            Image(systemName: "tab-2")
                            Text("Tab2")
                        }

                        //Tab 3 contained my ViewWithSheet() and SheetView()
                        Tab3().tabItem {
                            Image(systemName: "tab-3")
                            Text("Tab3")
                        }
                    }
                } else {
                    LoadingView(loadingMessage: $fetchMessage)
                }
            }
        }.onAppear(perform: initializeApp)
    }
}

我现在基本上有同样的问题;我的表格是从登录视图调用的注册视图。当注册后,我的注册表格的底层视图从登录视图更改为“已登录”视图,这使得我的表格无法关闭 - 我认为是因为我的登录视图的“主体”不再更新,所以表格无法关闭。非常烦人的是,一个表格不能自行关闭。 - Jaap Weijland

1

尝试在主队列上显式执行此操作

func create() {
    store.createObject() { object, error in 
       if let _ = object {
           DispatchQueue.main.async {
              self.showingSheetView = false
           }
       }
       // think also about feedback on else case as well !!
    }
}

我尝试了你的建议,但不幸的是视图仍然没有消失。对象肯定有一个值,并且showingSheetView变量的值确实被设置为false,但是该表仍然没有消失。 - charlemagne_vi

0
想看看我用过的一些hacky方法吗?免责声明:可能对你不起作用,我也不一定推荐。但或许能帮助到有需要的人。
如果你添加了一个NavigationLink并保留了你的fullScreenCover,那么全屏覆盖将能够像你期望的那样自行解除。
为什么当你将NavigationLink添加到你的视图中时会发生这种情况呢?我不知道。我猜测它在某个地方创建了一个额外的引用。
将以下代码添加到你的body中,并保留你的sheet:
NavigationLink(destination: YOURVIEW().environmentObjects(), isActive: $showingSheetView) {}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接