iOS含状态栏视图截图问题:模拟器有效真机无效求助
解决物理设备上无法截取含状态栏的屏幕快照问题
嘿,我来帮你搞定这个困扰!你的代码在模拟器能正常运行,但物理设备上失效,主要有两个核心问题:
- 你调用
drawHierarchy(in:afterScreenUpdates:)时,错误地把view实例传给了in参数——这个参数需要的是一个CGRect(也就是视图的边界),模拟器对这种错误兼容性比较宽松,但物理设备会直接卡壳。 - 用
snapshotView再转图像的方式,在物理设备(尤其是带刘海的全面屏机型)上可能没法完整捕获状态栏内容,因为真机上状态栏的渲染层级和模拟器有差异。
先给你修正原代码的版本
把参数错误改掉,同时优化上下文创建的方式,适配物理设备:
extension UIScreen { class func screenshot() -> UIImage? { guard let screenSnapshotView = UIScreen.main.snapshotView(afterScreenUpdates: false) else { return nil } // 用带透明度和缩放参数的上下文创建方法,更适配不同设备 UIGraphicsBeginImageContextWithOptions(screenSnapshotView.bounds.size, screenSnapshotView.isOpaque, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } // 确保上下文一定会被释放 screenSnapshotView.drawHierarchy(in: screenSnapshotView.bounds, afterScreenUpdates: true) return UIGraphicsGetImageFromCurrentImageContext() } }
更推荐的现代写法(iOS 10+)
苹果在iOS 10推出了UIGraphicsImageRenderer,比旧的绘图上下文更简洁,兼容性也更好,能完美捕获包括状态栏在内的整个屏幕:
extension UIScreen { class func screenshot() -> UIImage { let screenBounds = UIScreen.main.bounds let renderer = UIGraphicsImageRenderer(bounds: screenBounds) return renderer.image { renderContext in // 直接让当前主窗口绘制层级,这样能完整捕获状态栏 UIApplication.shared.windows.first?.drawHierarchy(in: screenBounds, afterScreenUpdates: true) } } }
针对iOS 13+的适配补充
如果你的App要适配iOS 13及以上的多场景模式,UIApplication.shared.windows可能拿不到正确的窗口,改成下面的写法更稳妥:
extension UIScreen { class func screenshot() -> UIImage { let screenBounds = UIScreen.main.bounds let renderer = UIGraphicsImageRenderer(bounds: screenBounds) return renderer.image { _ in guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let mainWindow = windowScene.windows.first(where: { $0.isKeyWindow }) else { return } mainWindow.drawHierarchy(in: screenBounds, afterScreenUpdates: true) } } }
最后提醒下:如果是在后台尝试截图,可能需要额外的权限,但前台截图的话,上面的代码在所有物理设备上都能正常工作啦。
内容的提问来源于stack exchange,提问作者Forsan Sharabati




