如何以编程方式添加视图控制器到Window并实现全局视频屏保?
全局视频屏保实现方案(基于Player库)
我之前也碰到过一模一样的问题——定时器能触发打印,但Player的视频视图就是出现在AppDelegate的window上。核心问题通常出在视图层级配置、Player初始化逻辑或者window的状态判断上,下面给你一步步拆解解决:
1. 先理清楚核心问题点
你现在定时器已经工作,但视频不显示,可能的原因包括:
- 视频视图没有被正确添加到window的顶层层级
- 视图frame没有适配window的尺寸
- Player实例没有正确绑定视频源,或者没有触发播放
- window本身还不是keyWindow(比如App启动初期)
2. 完整实现代码(AppDelegate中)
首先在AppDelegate里定义一个属性保存Player实例,避免被提前释放:
import Player @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? // 保存屏保Player实例 private var screensaverPlayer: Player? // 屏保定时器 private var screensaverTimer: Timer? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 初始化定时器(5秒触发) setupScreensaverTimer() return true } // 配置屏保定时器 private func setupScreensaverTimer() { screensaverTimer?.invalidate() screensaverTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(triggerScreensaver), userInfo: nil, repeats: false) } // 触发屏保显示 @objc private func triggerScreensaver() { print("Play Video") showVideoScreensaver() } // 显示视频屏保核心逻辑 private func showVideoScreensaver() { // 确保window存在且是可用状态 guard let keyWindow = UIApplication.shared.keyWindow ?? window else { print("Window not available") return } // 先移除旧的屏保视图(避免重复添加) screensaverPlayer?.view.removeFromSuperview() // 1. 初始化Player并配置视频源 let player = Player() // 替换成你的视频路径(本地/远程都可以) guard let videoURL = Bundle.main.url(forResource: "screensaver_video", withExtension: "mp4") else { print("Video file not found") return } player.url = videoURL // 2. 配置视图布局,确保铺满整个屏幕 player.view.frame = keyWindow.bounds // 设置自动适配屏幕尺寸变化 player.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] // 3. 添加到window并置顶(避免被其他视图遮挡) keyWindow.addSubview(player.view) keyWindow.bringSubviewToFront(player.view) // 4. 启动播放并设置循环 player.play() player.isLooping = true // 5. 保存实例引用,避免被释放 screensaverPlayer = player // 6. 添加退出逻辑(比如点击屏幕关闭屏保) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideScreensaver)) player.view.addGestureRecognizer(tapGesture) } // 隐藏屏保并重置定时器 @objc private func hideScreensaver() { screensaverPlayer?.pause() screensaverPlayer?.view.removeFromSuperview() screensaverPlayer = nil // 用户操作后重置定时器 setupScreensaverTimer() } // 处理用户交互,重置定时器(比如触摸、滑动等操作) func application(_ application: UIApplication, touchesBegan touches: Set<UITouch>, with event: UIEvent?) { setupScreensaverTimer() } }
3. 关键注意事项
- Player实例的生命周期:一定要用属性保存Player引用,不然方法执行完实例就被释放了,视图自然消失。
- window的正确性:优先用
UIApplication.shared.keyWindow,如果启动初期keyWindow还没生成,就用AppDelegate自身的window属性。 - 视图层级置顶:必须调用
bringSubviewToFront,不然可能被rootViewController的视图遮挡。 - 视频源验证:确保视频文件存在,路径正确,Player支持该格式(MP4是没问题的)。
- 用户交互重置定时器:要在用户有任何操作时重置定时器,不然屏保会一直触发。
4. 调试技巧
如果还是不显示,可以试试这些排查步骤:
- 打印
keyWindow.subviews,看看Player的view是否在列表里 - 给Player的view设置一个背景色(比如
player.view.backgroundColor = .red),确认视图是否真的被添加了 - 监听Player的
playerDidStartPlaying、playerDidFinishPlaying等回调,检查播放状态是否正常
内容的提问来源于stack exchange,提问作者Gavin
相关产品推荐
相关产品推荐

