我对 SwiftUI 还很陌生,因此非常感谢您的帮助。我有一个应用程序,其数据是私有的并且需要登录屏幕。我希望每次应用程序进入后台时都显示登录屏幕,但我需要应用程序保持状态(即,如果用户正在创建新项目并更改到另一个应用程序,我想要所有他输入的要保留的数据,如果应用程序被终止,我可以忍受数据丢失,但我想允许在应用程序之间暂时交换,以让用户在应用程序之间复制和粘贴数据)。
我一直在谷歌搜索解决方案,人们正在使用常规视图实现登录屏幕并根据某些状态变量显示它,但我发现这种方法效果不佳,因为:
我在一篇文章中读到(尽管它没有显示任何代码示例),方法是使用登录屏幕创建另一个场景并在场景之间切换,但我无法使用 SwiftUI 实现它。
到目前为止,我尝试通过让我的应用程序有两个 WindowGroup 来实现它,一个包含应用程序内容,另一个包含登录屏幕。就像下面这样:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup("MainScene") {
MainView()
}
WindowGroup("LoginScene") {
LoginScreenView()
}
}
}
但我不知道如何在场景之间进行切换。
据我所知,您不能在一个应用程序中拥有多个
WindowGroup
。请参阅 此 WWDC 视频,了解您可以选择哪些顶级应用程序容器以及它们的用途。
正如评论中已经提到的,这取决于应用程序状态。您可以观察
scenePhase
环境属性的变化并相应地设置状态。
例如:
// pull the scenePhase var from the environment
@Environment(\.scenePhase) private var scenePhase
// this will hold the login state of the application
@State private var login: Bool = false
var body: some Scene {
WindowGroup {
Group{
if login{
// if the user is logged in show the content
ContentView()
} else{
// Here should be some view with appropriate logic
Button("login"){
login = true
}
}
}
// observe all scenePhase changes and invalidate the login
.onChange(of: scenePhase) { newValue in
login = false
}
}
}
在
ContentView
中,现在归结为保留之前的状态。您可以使用 @SceneStorage
或 @AppStorage
来保留要再次显示的值。由于您没有提到如何导航或显示这些视图,我只能推测。我提供了 2 个示例来说明如何实现这一点。
带有
NavigationStack
:
struct ContentView: View {
@SceneStorage("navigation") var path: [String] = []
var body: some View {
NavigationStack(path: $path) {
Button("subView"){
path.append("subView")
}
.navigationDestination(for: String.self) { value in
if value == "subView"{
SubView()
}
}
}
}
}
struct SubView: View {
// persist the values with @SceneStorage
@SceneStorage("text1") private var text1: String = ""
@SceneStorage("text2") private var text2: String = ""
var body: some View {
VStack{
Text("subView")
TextField("", text: $text1)
.textFieldStyle(.roundedBorder)
.padding()
TextField("", text: $text2)
.textFieldStyle(.roundedBorder)
.padding()
}
}
}
// this extension is used to store an array of any Codable in UserDefaults
extension Array: RawRepresentable where Element: Codable{
public typealias RawValue = String
// encode the array or save empty array if it fails
public var rawValue: RawValue{
guard let data = try? JSONEncoder().encode(self), let coded = String(data: data, encoding: .utf8) else {
return "[]"
}
return coded
}
// decode the string to an array or empty if it fails
public init?(rawValue: RawValue) {
guard let data = rawValue.data(using: .utf8), let decoded = try? JSONDecoder().decode(Self.self, from: data) else {
self = []
return
}
self = decoded
}
}
带有一张显示
SubView
的表格。在这里,我们需要存储自己显示和隐藏工作表的值。当关闭视图或将视图移动到背景时,SwiftUI 会将值设置为 false
。
struct ContentView: View {
// load the saved value from UserDefaults
@State private var showSheet: Bool = UserDefaults.standard.bool(forKey: "sheet")
var body: some View {
Button("show sheet"){
showSheet = true
}.sheet(isPresented: $showSheet, content: {
SubView()
})
.onChange(of: showSheet) { newValue in
// store changed value to UserDefaults
UserDefaults.standard.setValue(newValue, forKey: "sheet")
}
}
}
这当然只是一个粗略的实现,还可以进行大量的改进。但它应该让你朝着正确的方向前进。