Ios 当应用程序被放到后台时,SwiftUI应用程序返回到第一个屏幕
我在SwiftUI中构建了一个应用程序,遇到了一个恼人的问题 当应用程序出现时,应用程序将显示一个自定义启动屏幕,同时应用程序的数据将异步加载。完成后,应用程序的主视图将显示给用户。一旦用户到达这一点,只要他们将应用程序拖回后台,导航堆栈就会毫无原因地跳回第一个启动屏幕 以下是相关的代码片段: MyApp.swiftIos 当应用程序被放到后台时,SwiftUI应用程序返回到第一个屏幕,ios,swiftui,Ios,Swiftui,我在SwiftUI中构建了一个应用程序,遇到了一个恼人的问题 当应用程序出现时,应用程序将显示一个自定义启动屏幕,同时应用程序的数据将异步加载。完成后,应用程序的主视图将显示给用户。一旦用户到达这一点,只要他们将应用程序拖回后台,导航堆栈就会毫无原因地跳回第一个启动屏幕 以下是相关的代码片段: MyApp.swift @main struct Jam500App: App { @Environment(\.scenePhase) var lifecycle init(
@main
struct Jam500App: App {
@Environment(\.scenePhase) var lifecycle
init() {
//initial setup goes here
}
var body: some Scene {
WindowGroup {
LaunchView()
}
}
}
启动视图
struct LaunchView: View {
@ObservedObject var control: LaunchViewControl = LaunchViewControl()
var body: some View {
NavigationView(content: {
ZStack {
//some view hierarchy
NavigationLink(destination: RootView(),
isActive: $control.isReady,
label: {EmptyView()})
}
.onAppear(perform: {
control.loadData()
})
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
}).navigationViewStyle(StackNavigationViewStyle())
}
}
struct RootView: View {
@ObservedObject var control = RootViewControl()
var body: some View {
HStack(spacing: 0) {
//some view hierarchy
}
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
}
}
LaunchViewControl.swift
final class LaunchViewControl: ObservableObject {
@Published var isReady: Bool = false
private var executing: Bool = false
private let dataService = DataService.shared
func loadData() {
//runs all initial app setup needed before the app starts (asynchronously)
guard !executing else {
return
}
executing = true
self.setupCoreData()
}
private func setupCoreData() {
//Initialises CoreData with CloudKit (asynchronously)
CloudCoreDataService.shared.setup { [weak self] in
guard let self = self else { return }
self.checkRequiresInitialData()
}
}
private func checkRequiresInitialData() {
self.dataService.requiresInitialDataSetup(onCompletion: { [weak self] (isRequired) in
guard let self = self else { return }
if isRequired {
self.setupInitialData()
} else {
self.isReady = true
}
})
}
private func setupInitialData() {
//Generates initial database if needed
self.dataService.setupInitialData { [weak self] (_) in
guard let self = self else { return }
self.isReady = true
}
}
}
RootView.swift
struct LaunchView: View {
@ObservedObject var control: LaunchViewControl = LaunchViewControl()
var body: some View {
NavigationView(content: {
ZStack {
//some view hierarchy
NavigationLink(destination: RootView(),
isActive: $control.isReady,
label: {EmptyView()})
}
.onAppear(perform: {
control.loadData()
})
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
}).navigationViewStyle(StackNavigationViewStyle())
}
}
struct RootView: View {
@ObservedObject var control = RootViewControl()
var body: some View {
HStack(spacing: 0) {
//some view hierarchy
}
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
}
}
是否有其他人在Xcode 12.4中遇到过此问题?有两种解决方案(我想到了,不会引起太多更改),您可以应用:
1.注射
在视图中初始化“LaunchViewControl”类时:
@ObservedObject var control: LaunchViewControl = LaunchViewControl()
SwiftUI在其视图被丢弃和重绘时会反复初始化此控制变量,因为ObservedObject与状态变量不同,SwiftUI不会跟踪它们的状态
要尝试此方法,请取消对“scenePhase”行的注释,并将“init”方法添加到“LaunchViewControl”,然后添加一些print语句以了解发生了什么,或者添加一个断点
例如:
final class LaunchViewControl: ObservableObject {
// ....
init() {
print("initialized")
}
// ....
}
您将在控制台上看到多个“已初始化”日志。这意味着,“isReady”变量将一次又一次地被覆盖为“false”
要防止这种情况并保持生命周期事件,可以使用基本注入
例如,将“启动视图”更改为:
struct LaunchView: View {
// Change here
@ObservedObject var control: LaunchViewControl
var body: some View {
NavigationView(content: {
ZStack {
//some view hierarchy
Text("Launch")
NavigationLink(destination: RootView(),
isActive: $control.isReady,
label: {EmptyView()})
}
.onAppear(perform: {
control.loadData()
})
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
}).navigationViewStyle(StackNavigationViewStyle())
}
}
以及您的“Jam500App”到:
@main
struct Jam500App: App {
@Environment(\.scenePhase) var scenePhase
let launchViewControl = LaunchViewControl()
var body: some Scene {
WindowGroup {
// Inject launchViewControl
LaunchView(control: launchViewControl)
}
}
}
您现在应该看到第二个视图没有弹出,并且您还保留了生命周期事件
2.状态对象
您可以将ObservedObject更改为“StateObject”,而不是全部执行这些操作,当视图被丢弃并重新绘制时,它将保持其状态
更多信息
此外,还有一些很好的答案,您可以查看观察到的对象以及它们是如何工作的。其中之一是。另一个关于StateObject和ObservedObject之间的区别是
为了防止在项目的后续步骤中出现这种情况,我会尝试一下MVVM方法,如果您想进一步了解它,例如,这里有一些很棒的教程。有两种解决方案(我想到了,并且不会引起太多更改),您可以应用:
1.注射
在视图中初始化“LaunchViewControl”类时:
@ObservedObject var control: LaunchViewControl = LaunchViewControl()
SwiftUI在其视图被丢弃和重绘时会反复初始化此控制变量,因为ObservedObject与状态变量不同,SwiftUI不会跟踪它们的状态
要尝试此方法,请取消对“scenePhase”行的注释,并将“init”方法添加到“LaunchViewControl”,然后添加一些print语句以了解发生了什么,或者添加一个断点
例如:
final class LaunchViewControl: ObservableObject {
// ....
init() {
print("initialized")
}
// ....
}
您将在控制台上看到多个“已初始化”日志。这意味着,“isReady”变量将一次又一次地被覆盖为“false”
要防止这种情况并保持生命周期事件,可以使用基本注入
例如,将“启动视图”更改为:
struct LaunchView: View {
// Change here
@ObservedObject var control: LaunchViewControl
var body: some View {
NavigationView(content: {
ZStack {
//some view hierarchy
Text("Launch")
NavigationLink(destination: RootView(),
isActive: $control.isReady,
label: {EmptyView()})
}
.onAppear(perform: {
control.loadData()
})
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
}).navigationViewStyle(StackNavigationViewStyle())
}
}
以及您的“Jam500App”到:
@main
struct Jam500App: App {
@Environment(\.scenePhase) var scenePhase
let launchViewControl = LaunchViewControl()
var body: some Scene {
WindowGroup {
// Inject launchViewControl
LaunchView(control: launchViewControl)
}
}
}
您现在应该看到第二个视图没有弹出,并且您还保留了生命周期事件
2.状态对象
您可以将ObservedObject更改为“StateObject”,而不是全部执行这些操作,当视图被丢弃并重新绘制时,它将保持其状态
更多信息
此外,还有一些很好的答案,您可以查看观察到的对象以及它们是如何工作的。其中之一是。另一个关于StateObject和ObservedObject之间的区别是
为了防止在项目的后续步骤中出现这种情况,我会尝试一下MVVM方法,如果您想了解更多,例如,这里有很多很棒的教程。只是为了检查一下,当您在Jam500App类中注释掉“@Environment(\.scenePhase)var lifecycle”行时,行为会发生变化,对吗?谢谢Burak,我从应用程序中删除了代码scenePhase,它成功了!这真的很烦人。如果没有它,我如何处理应用程序生命周期事件?只是想检查一下,当您在Jam500App类中注释掉“@Environment(\.scenePhase)var lifecycle”行时,行为会发生变化,对吗?谢谢Burak,我从应用程序中删除了代码scenePhase,它成功了!这真的很烦人。如果没有它,我如何处理应用程序生命周期事件?谢谢你,Burak,这是对我问题的一个很好的回答!谢谢你,Burak,这是对我问题的一个很好的回答!