什么是 ScenePhaseh1
在 iOS 应用程序中,ScenePhase 可以帮助我们确定应用程序当前处于哪个状态,并帮助我们在状态转换时做出响应。它主要用于多任务处理,特别是在多窗口应用程序中。
使用 ScenePhase 值可以帮助我们判断应用程序是否处于前台、后台或暂停状态,这有助于我们管理应用程序的资源和数据,并及时响应应用程序的状态变化。
ScenePhase 有四个可能的值:
- active:表示场景目前处于前台并正在与用户进行交互。
- background:表示场景目前在后台运行,并且用户无法看到其内容。
- inactive:表示场景当前处于过渡状态。例如,当用户从另一个应用程序切换回我们的应用程序时,它将被设置为此状态。
- unknown:表示我们尚不知道场景的当前状态。通常,这只会在场景刚刚启动时发生。
下面是一个简单的示例,演示了如何在 SwiftUI 中使用 ScenePhase:
struct ContentView: View { @Environment(\.scenePhase) private var scenePhase
var body: some View { VStack { Text("Hello, world!") } .onChange(of: scenePhase) { newScenePhase in switch newScenePhase { case .active: print("App is active") case .inactive: print("App is inactive") case .background: print("App is in background") @unknown default: print("Unknown scene phase") } } }}在这个示例中,我们使用了 @Environment(\.scenePhase) 属性包装器,它允许我们在视图中访问当前场景的 ScenePhase 值。我们还使用了 .onChange 修饰符,以便在场景状态变化时触发回调。
离谱的 Bugh1
在我的主力机上面,scenePhase的值运行非常正确且及时。但在我备用机上发生了离谱的Bug,当我的应用进入后台时,scenePhase并不会及时更新为.background而是在重新返回应用时,才会变为.background并快速变为.active,导致我的代码出现严重的逻辑错误。
在我花了n个小时排查后,才发现这是 iOS 15.6 中的一个已知问题😭(浪费时间白忙活…)
为了避免在 iOS 15.6 的设备上解决这个问题,我们需要使用 NotificationCenter 监听应用程序状态变化,并在状态变化时更新我们的 UI。
具体来说,我们可以使用以下代码:
//新建一个 extension
extension View { #if os(iOS) func onBackground(_ f: @escaping () -> Void) -> some View { self.onReceive( NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification), perform: { _ in f() } ) }
func onForeground(_ f: @escaping () -> Void) -> some View { self.onReceive( NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification), perform: { _ in f() } ) } #else func onBackground(_ f: @escaping () -> Void) -> some View { self.onReceive( NotificationCenter.default.publisher(for: NSApplication.willResignActiveNotification), perform: { _ in f() } ) }
func onForeground(_ f: @escaping () -> Void) -> some View { self.onReceive( NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification), perform: { _ in f() } ) } #endif}然后就可以在对应的视图中使用:
AppView() .onBackground { print("my background") } .onForeground { print("my foreground") }这个 iOS15.6 的 Bug 导致 ScenePhase 的方法废了,因为你无法假设所有用户的系统到都是最新的,所以对于判断应用程序是否处于前台、后台或暂停状态的需求,使用上述的 extension 方法是再合适不过了。