返回

SwiftUI 语言切换后重启应用的最佳实践

IOS

SwiftUI 应用在切换语言后重启并应用新语言设置,需要一种可靠的机制来触发应用的完全重启。虽然修改 @AppStorage 或使用 .id 可以刷新视图,但这并不能真正重启应用,导致部分界面元素无法正确更新。 以下提供几种解决方案,并分析其优缺点。

解决方案一:利用 exit(0)

最直接的方案是使用 exit(0) 函数终止应用进程。系统会重新启动应用,并加载新的语言设置。

func setLanguage(_ language: Language) {
    // ... (语言设置逻辑) ...

    exit(0)
}

操作步骤:

  1. setLanguage 函数的最后调用 exit(0)

优点: 简单直接,可以确保应用完全重启。

缺点: 用户体验不佳,重启过程较为突兀。此外,Apple 官方不建议使用这种方式,因为它绕过了正常的应用生命周期。

解决方案二:模拟 Scene 变化 (推荐)

通过设置一个新的 ScenePhase,我们可以模拟应用进入后台,然后再次进入前台,从而触发 SwiftUI 的视图重建。这种方法更符合应用生命周期,用户体验也更好。

首先,需要访问 ScenePhase。一种方法是创建一个 ObservableObject 来包装它。

class ScenePhaseObserver: ObservableObject {
    @Published var phase: ScenePhase = .active

    init() {
        NotificationCenter.default.addObserver(self, selector: #selector(updatePhase(_:)), name: UIScene.didActivateNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(updatePhase(_:)), name: UIScene.willDeactivateNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(updatePhase(_:)), name: UIScene.didEnterBackgroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(updatePhase(_:)), name: UIScene.willEnterForegroundNotification, object: nil)
    }

    @objc private func updatePhase(_ notification: Notification) {
        if let scenePhase = notification.userInfo?[UIScene.phaseUserInfoKey] as? UIScene.ActivationState,
           let newPhase = ScenePhase(rawValue: scenePhase.rawValue) {
                phase = newPhase
        }
    }
}

然后在 App 中使用:

@main
struct YourApp: App {
    @StateObject var scenePhaseObserver = ScenePhaseObserver()

    var body: some Scene {
        WindowGroup {
           // ... Your content view ...
            .onChange(of: scenePhaseObserver.phase) { newPhase in
                if newPhase == .background {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {  //  短暂延时
                        scenePhaseObserver.phase = .active
                    }
                }
            }
        }
        .onChange(of: localizationManager.currentLanguage) { _ in
            scenePhaseObserver.phase = .background
        }
    }
}

操作步骤:

  1. 创建 ScenePhaseObserver 类。
  2. App 中实例化 ScenePhaseObserver
  3. 监听 currentLanguage 的变化,并在变化时将 scenePhase 设置为 .background,再迅速切换回 .active,模拟应用重启。

优点: 用户体验更好,符合应用生命周期,避免了强制退出。

缺点: 实现稍微复杂一些。

额外的建议

  • 可以考虑在应用启动时检查语言设置,避免每次都进行不必要的重启。
  • 在切换语言时,可以提供一些视觉反馈,例如短暂的加载动画,告知用户应用正在重新加载。
  • 务必测试不同 iOS 版本和设备上的兼容性。

通过以上方案,可以有效地实现 SwiftUI 应用在语言切换后的重启,并确保所有界面元素都能正确更新。选择哪种方案取决于项目的具体需求和对用户体验的要求。 记住,方案二更符合 Apple 的最佳实践,也是推荐的方案。

避免使用UIApplication.shared.windows.first?.rootViewController = UIHostingController(rootView: YourRootView())这样的操作。这种方式直接操作底层 UIKit,可能会导致不可预知的行为,并且与 SwiftUI 的声明式编程范式相冲突,难以维护。