iOS开发求助:如何避免截图中显示真实UI内容?
问题原因
你当前使用的UIApplication.userDidTakeScreenshotNotification是截图完成后才触发的通知,此时添加的红色遮挡层不会被包含到已经完成的截图里,这就是保存的图片仍显示真实UI的核心原因。
解决方案
需要改用UIWindowSceneDelegate的windowScene(_:performActionFor:identifier:completionHandler:)方法,这个方法会在系统执行截图动作之前被调用,能让我们提前添加遮挡层,确保截图包含遮挡内容,之后再移除遮挡层不影响用户操作。
修改后的完整代码
class HomeViewController: UIViewController { private var greetLabel: UILabel! private var screenshotBlockerView: ScreenshotBlockerView? // 保存遮挡层引用,方便后续移除 private let navigator: HomeNavigator init(navigator: HomeNavigator) { self.navigator = navigator super.init(nibName: nil, bundle: nil) setupTabBar() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() setupNavigationBar() setupViews() setupConstraints() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 绑定当前页面为windowScene的代理,拦截截图动作 if let windowScene = view.window?.windowScene { windowScene.delegate = self } } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) // 恢复原有代理,避免影响其他页面逻辑 if let windowScene = view.window?.windowScene, let originalDelegate = UIApplication.shared.connectedScenes.first?.delegate as? UIWindowSceneDelegate { windowScene.delegate = originalDelegate } // 确保遮挡层被移除 removeScreenshotBlockerView() } // 添加遮挡层(支持全屏或特定视图) private func addScreenshotBlockerView() { // 方案1:全屏遮挡 let frame = UIScreen.main.bounds // 方案2:仅遮挡特定视图(比如greetLabel) // let frame = greetLabel.convert(greetLabel.bounds, to: UIApplication.shared.keyWindow) screenshotBlockerView = ScreenshotBlockerView(frame: frame) if let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) { keyWindow.addSubview(screenshotBlockerView!) } // 强制刷新UI,确保遮挡层立即生效 UIView.performWithoutAnimation { screenshotBlockerView?.layoutIfNeeded() } } // 移除遮挡层 func removeScreenshotBlockerView() { screenshotBlockerView?.removeFromSuperview() screenshotBlockerView = nil } // 原有页面配置方法保持不变 private func setupNavigationBar() { navigationItem.title = "Home" self.navigationItem.largeTitleDisplayMode = .never navigationController?.navigationBar.prefersLargeTitles = true navigationItem.backBarButtonItem = UIBarButtonItem(title: "",style: .plain,target: nil,action: nil) } private func setupTabBar() { let tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house") , tag: 0) tabBarItem.selectedImage = UIImage(systemName: "house.fill") self.tabBarController?.tabBar.barTintColor = UIColor.blue self.tabBarItem = tabBarItem } private func setupViews() { view.backgroundColor = .white let greetLabel = UILabel() greetLabel.numberOfLines = 0 greetLabel.font = UIFont.systemFont(ofSize: 18 , weight: UIFont.Weight.semibold) greetLabel.textColor = UIColor.black greetLabel.translatesAutoresizingMaskIntoConstraints = false greetLabel.text = AppConstants.shared.greetLabelText(forType: .itemConstant) self.greetLabel = greetLabel view.addSubview(greetLabel) } private func setupConstraints() { NSLayoutConstraint.activate([ self.greetLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), self.greetLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor), ]) } } // 扩展实现UIWindowSceneDelegate,拦截截图动作 extension HomeViewController: UIWindowSceneDelegate { func windowScene(_ windowScene: UIWindowScene, performActionFor session: UISceneSession, identifier: UISceneSession.ActionIdentifier, completionHandler: @escaping (UISceneSession.ActionResponse) -> Void) { // 判断是否是截图动作 if identifier == .takeScreenshot { // 截图前添加遮挡层 addScreenshotBlockerView() // 截图完成后延迟移除遮挡层(0.1秒足够完成截图) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.removeScreenshotBlockerView() } // 允许系统继续完成截图流程 completionHandler(.continue) return } // 其他系统动作交给默认处理逻辑 completionHandler(.continue) } } class ScreenshotBlockerView: UIView { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = UIColor.red // 可选:添加自定义遮挡内容,比如替换真实文本的占位符 let placeholderLabel = UILabel(frame: bounds) placeholderLabel.text = "截图保护内容" placeholderLabel.textColor = .white placeholderLabel.textAlignment = .center placeholderLabel.font = UIFont.systemFont(ofSize: 18, weight: .semibold) addSubview(placeholderLabel) } required init?(coder: NSCoder) { super.init(coder: coder) } }
关键说明
- 拦截时机:通过
windowScene(_:performActionFor:identifier:completionHandler:)在截图前触发遮挡层添加,确保截图包含遮挡内容。 - 代理恢复:在页面消失时恢复原有
windowScene代理,避免影响其他页面的系统交互逻辑。 - 灵活遮挡:可以选择全屏遮挡,或者仅遮挡特定视图(比如示例中的
greetLabel),只需修改addScreenshotBlockerView中的frame计算逻辑。
内容的提问来源于stack exchange,提问作者Naveen Kumar
相关产品推荐
相关产品推荐

