什么是 ScenePhase 在 iOS 应用程序中,ScenePhase 可以帮助我们确定应用程序当前处于哪个状态,并帮助我们在状态转换时做出响应。它主要用于多任务处理,特别是在多窗口应用程序中。
使用 ScenePhase 值可以帮助我们判断应用程序是否处于前台、后台或暂停状态,这有助于我们管理应用程序的资源和数据,并及时响应应用程序的状态变化。
ScenePhase 有四个可能的值:
active:表示场景目前处于前台并正在与用户进行交互。
background:表示场景目前在后台运行,并且用户无法看到其内容。
inactive:表示场景当前处于过渡状态。例如,当用户从另一个应用程序切换回我们的应用程序时,它将被设置为此状态。
unknown:表示我们尚不知道场景的当前状态。通常,这只会在场景刚刚启动时发生。
下面是一个简单的示例,演示了如何在 SwiftUI 中使用 ScenePhase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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
修饰符,以便在场景状态变化时触发回调。
离谱的 Bug 在我的主力机上面,scenePhase
的值运行非常正确且及时。但在我备用机上发生了离谱的Bug,当我的应用进入后台时,scenePhase
并不会及时更新为.background
而是在重新返回应用时,才会变为.background
并快速变为.active
,导致我的代码出现严重的逻辑错误。
在我花了n个小时排查后,才发现这是 iOS 15.6 中的一个已知问题😭(浪费时间白忙活……)
为了避免在 iOS 15.6 的设备上解决这个问题,我们需要使用 NotificationCenter 监听应用程序状态变化,并在状态变化时更新我们的 UI。
具体来说,我们可以使用以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 }
然后就可以在对应的视图中使用:
1 2 3 4 5 6 7 AppView () .onBackground { print ("my background" ) } .onForeground { print ("my foreground" ) }
这个 iOS15.6 的 Bug 导致 ScenePhase 的方法废了,因为你无法假设所有用户的系统到都是最新的,所以对于判断应用程序是否处于前台、后台或暂停状态的需求,使用上述的 extension 方法是再合适不过了。