You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI:App回到前台后外接屏幕视图无法更新

问题描述

我开发了一款和弦与歌词应用,需要通过外接屏幕实现歌词投影功能。App在切换至后台再返回前台前运行正常,但返回后外接屏幕的视图不再更新——仅初始显示时数值正确,后续完全无变化。

Info.plist 配置

  • EnableMultipleWindows = YES
  • 场景配置(SceneConfiguration):
    • 角色:External Display Session Role Non-Interactive
    • 配置名称:External
    • 代理类名:$(PRODUCT_NAME).ExternalSceneDelegate

外接屏幕测试视图代码

struct ExternalScreenTestView: View {
    @ObservedObject var counter = TestCounter.shared
    
    var body: some View {
        Text(counter.count.description)
            .font(.system(size: 50))
    }
}

class TestCounter: ObservableObject {
    private var timer: Timer?
    @Published var count = 1
    
    static var shared:TestCounter = TestCounter()
    
    init() {
        self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.count += 1
        }
    }
}

ExternalSceneDelegate 代码

final class ExternalSceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    var controller: UIHostingController<ExternalScreenTestView>?
    
    func scene(_ scene: UIScene,
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) {
        
        guard let scene = scene as? UIWindowScene else {
            return
        }
        
        let content = ExternalScreenTestView()
        window = UIWindow(windowScene: scene)
        controller = UIHostingController(rootView: content)
        window?.rootViewController = controller
        window?.isHidden = false
    }
}
解决方案

1. 确保@Published属性在主线程更新

定时器闭包默认运行在非主线程,直接更新@Published属性无法触发UI刷新。修改TestCounter的定时器逻辑,将属性更新操作切换到主线程:

class TestCounter: ObservableObject {
    private var timer: Timer?
    @Published var count = 1
    
    static var shared: TestCounter = TestCounter()
    
    init() {
        self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
            DispatchQueue.main.async {
                self?.count += 1
            }
        }
    }
}

2. 处理外部场景的激活事件

App从后台返回时,外部场景可能需要重新绑定视图以恢复订阅。修改ExternalSceneDelegate,添加场景激活时的视图重置逻辑:

final class ExternalSceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    var controller: UIHostingController<ExternalScreenTestView>?
    
    func scene(_ scene: UIScene,
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) {
        
        guard let scene = scene as? UIWindowScene else {
            return
        }
        setupExternalWindow(for: scene)
    }
    
    func sceneDidBecomeActive(_ scene: UIScene) {
        guard let scene = scene as? UIWindowScene else {
            return
        }
        setupExternalWindow(for: scene)
    }
    
    private func setupExternalWindow(for scene: UIWindowScene) {
        let contentView = ExternalScreenTestView()
        window = UIWindow(windowScene: scene)
        controller = UIHostingController(rootView: contentView)
        window?.rootViewController = controller
        window?.isHidden = false
    }
}

3. 优化视图的ObservableObject绑定

如果不需要跨场景共享计数器实例,建议将视图中的@ObservedObject替换为@StateObject,它能更稳定地管理对象生命周期:

struct ExternalScreenTestView: View {
    @StateObject var counter = TestCounter()
    
    var body: some View {
        Text(counter.count.description)
            .font(.system(size: 50))
    }
}

若必须保持单例共享,保留@ObservedObject var counter = TestCounter.shared即可。


内容的提问来源于stack exchange,提问作者MarekP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:41:12