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

如何在SwiftUI中创建独立UIWindow以规避ReplayKit主窗口录制限制?

在SwiftUI中创建独立UIWindow的解决方案

ReplayKit录屏仅会捕获应用的主窗口,因此可以将不想被录屏的UI元素放置在独立的UIWindow对象中。iOS支持多窗口机制,但每个UIWindow需要通过rootViewController提供内容。你之前的代码创建窗口失败的核心原因是:新创建的UIWindow是局部变量,按钮闭包执行完毕后就会被系统内存回收,导致窗口无法持续显示。

修正方案

需要将新创建的UIWindow保存为持久化引用,避免被系统回收。以下提供两种可行的实现方式:

方法1:全局变量存储窗口

import SwiftUI

// 全局容器存储创建的窗口,保持引用避免被释放
var extraWindows: [UIWindow] = []

extension UIApplication {
    var activeWindowScene: UIWindowScene? {
        connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .first(where: { $0 is UIWindowScene }) as? UIWindowScene
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            Button("打开新窗口") {
                guard let windowScene = UIApplication.shared.activeWindowScene else { return }
                
                let newWindow = UIWindow(frame: UIScreen.main.bounds)
                newWindow.windowScene = windowScene
                // 设置窗口层级,确保显示在主窗口之上(可按需调整)
                newWindow.windowLevel = .alert + 1
                newWindow.rootViewController = UIHostingController(rootView: 
                    Text("这是独立窗口的内容!")
                        .font(.largeTitle)
                        .foregroundColor(.white)
                        .background(Color.blue)
                )
                newWindow.makeKeyAndVisible()
                
                // 将窗口加入全局容器
                extraWindows.append(newWindow)
            }
            
            Button("关闭所有额外窗口") {
                extraWindows.forEach { $0.isHidden = true }
                extraWindows.removeAll()
            }
        }
        .padding()
    }
}

方法2:使用ObservableObject管理窗口

如果更贴合SwiftUI的状态管理逻辑,可以用ObservableObject来统一管理窗口:

import SwiftUI

class WindowManager: ObservableObject {
    static let shared = WindowManager()
    private(set) var extraWindows: [UIWindow] = []
    
    func createNewWindow(in scene: UIWindowScene, content: some View) {
        let newWindow = UIWindow(frame: UIScreen.main.bounds)
        newWindow.windowScene = scene
        newWindow.windowLevel = .alert + 1
        newWindow.rootViewController = UIHostingController(rootView: content)
        newWindow.makeKeyAndVisible()
        extraWindows.append(newWindow)
    }
    
    func closeAllWindows() {
        extraWindows.forEach { $0.isHidden = true }
        extraWindows.removeAll()
    }
}

extension UIApplication {
    var activeWindowScene: UIWindowScene? {
        connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .first(where: { $0 is UIWindowScene }) as? UIWindowScene
    }
}

struct ContentView: View {
    @StateObject private var windowManager = WindowManager.shared
    
    var body: some View {
        VStack {
            Button("打开新窗口") {
                guard let windowScene = UIApplication.shared.activeWindowScene else { return }
                windowManager.createNewWindow(in: windowScene, content: 
                    Text("这是通过状态管理创建的独立窗口")
                        .font(.title)
                        .foregroundColor(.black)
                        .background(Color.yellow)
                )
            }
            
            Button("关闭所有额外窗口") {
                windowManager.closeAllWindows()
            }
        }
        .padding()
    }
}

关键说明

  • 保持窗口引用:必须将新UIWindow存储在不会被立即释放的容器中(全局数组、ObservableObject属性等),否则窗口会被系统回收消失。
  • 窗口层级:通过windowLevel可以调整窗口的显示优先级,比如.alert + 1能让窗口显示在系统弹窗之上,可根据需求灵活设置。
  • ReplayKit适配:这种独立窗口不会被ReplayKit录屏捕获,正好适配你不想被录屏的UI元素放置需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:38:16