SwiftUI/iOS 15:全屏封面不会关闭,绑定已发布的变量



My App 使用 SwiftUI,面向 iOS 15+,由ObservableObjectAppState管理,该 是整个应用程序的事实来源。我使用@Published vars 来管理各个地方的叠加状态(例如,在加载数据时),没有问题。

简化AppState

class AppState:ObservableObject {
@Published var showLoadingOverlay = false
func loadData() {
self.showLoadingOverlay = true
// fech data
self.showLoadingOverlay = false
}
}

在视图中,我通过@EnvironmentObject注入AppState,并使用已发布的变量来控制fullScreenCover

简化示例:

struct MyDataView:View {
@EnvironmentObject var state:AppState
var body: some View {
ScrollView {
VStack {
ForEach(Array(zip(state.dataSets.indices, state.dataSets)), id: .0) { (dataSetIndex, dataSet) in
MirrorChartView(dataSet: dataSet)
}
}
}
.onAppear {
state.loadData()
}
.fullScreenCover(isPresented: $state.showLoadingOverlay) {
LoadingOverlay()
}
}
}

我遇到的问题是,当绑定值更改为false时,一个叠加层仍然显示。这种"卡住">只发生在loadData()完成得非常快的情况下,并且叠加层在完全显示之前几乎会被忽略。

将调试监视器添加到视图可确认正确传播到AppState的绑定:

struct MyDataView:View {
@EnvironmentObject var state:AppState
var body: some View {
VStack {
// draw stuff
}
.onAppear {
state.loadData()
}
.onChange(of: state.showLoadingOverlay) { showOverlay in
print("show overlay: (showOverlay)")
}
.fullScreenCover(isPresented: $state.showLoadingOverlay) {
LoadingOverlay()
}
}
}

即,当覆盖层向上滑动时,我可以看到日志show overlay: false,但它不会被关闭。

即使通过@State本地绑定添加间接寻址也不能可靠地解决问题:

struct MyDataView:View {
@EnvironmentObject var state:AppState
@State var showLoadingOverlay = false
var body: some View {
VStack {
// draw stuff
}
.onAppear {
state.loadData()
}
.onChange(of: state.showLoadingOverlay) { showOverlay in
self.showLoadingOverlay = showOverlay
}
.fullScreenCover(isPresented: self.$showLoadingOverlay) {
LoadingOverlay()
}
}
}

更新:更多的测试表明,即使就地绑定为假也不会消除fullScreenCover

将状态绑定替换为就地自定义绑定会显示我的状态已正确传播并导致视图更新。但是,工作表仍然保持显示。

struct MyDataView:View {
@EnvironmentObject var state:AppState
var body: some View {
VStack {
// draw stuff
}
.onAppear {
state.loadData()
}
.fullScreenCover(isPresented: .init(
get: {
print("recomputing binding: (state.showOverlay)")
return state.showOverlay
}, set: { newValue in
state.showOverlay = newValue
}
)) {
LoadingOverlay()
}
}
}

这将生成以下日志消息年表:

recomputing binding: false
recomputing binding: true
recomputing binding: false

最新情况:进一步调查结果

我不确定该怎么做,但是将日志消息添加到LoadingOverlay.onAppear()显示,它在绑定更改为 false出现:

struct MyDataView:View {
@EnvironmentObject var state:AppState
var body: some View {
ScrollView {
VStack {
ForEach(Array(zip(state.dataSets.indices, state.dataSets)), id: .0) { (dataSetIndex, dataSet) in
MirrorChartView(dataSet: dataSet)
}
}
}
.onAppear {
state.loadData()
}
.onChange(of: state.showLoadingOverlay) { show in
print("show loading overlay (show)")
}
.fullScreenCover(isPresented: $state.showLoadingOverlay) {
LoadingOverlay()
.onAppear {
print("loading overlay appears")
}
}
}
}

这将生成以下日志消息年表:

show loading overlay: true
show loading overlay: false
loading overlay appears

有哪些方法可以解决此问题?

尝试以LoadingOverlayonAppear()加载数据。这样,您可以延迟加载,并可以防止导致问题的任何情况:

import SwiftUI
class AppState: ObservableObject {
@Published var showLoadingOverlay = false

func loadData() {
//load data
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.showLoadingOverlay = false
}
}
}
struct LoadingOverlay: View {
@EnvironmentObject var state: AppState
var body: some View {
VStack {
Text("Loading")
}
.onAppear {
state.loadData()
}
}
}
struct ContentView: View {
@StateObject var state = AppState()
var body: some View {
VStack {
Text("Data View")
}
.onAppear {
state.showLoadingOverlay = true
}

.fullScreenCover(isPresented: $state.showLoadingOverlay) {
LoadingOverlay()
}
.environmentObject(state)
}
}

最新更新