如何识别QLPreviewController进入标记模式并控制导航栏按钮
解决QLPreviewController标记模式检测的方案
方法1:通过KVO监听导航栏按钮变化
进入标记模式后,QLPreviewController的导航栏按钮会切换为标记工具相关控件(如「完成」「撤销」按钮)。可以通过KVO监听导航栏按钮数组的变化,判断是否处于标记模式:
class CustomQLPreviewController: QLPreviewController { // 自定义导航栏按钮 private let customBarButtonItem = UIBarButtonItem(title: "自定义", style: .plain, target: nil, action: nil) // 记录当前标记模式状态,避免重复更新 private var isInMarkupMode = false override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = customBarButtonItem // 监听导航栏左右按钮变化 navigationItem.addObserver(self, forKeyPath: #keyPath(UINavigationItem.leftBarButtonItems), options: [.old, .new], context: nil) navigationItem.addObserver(self, forKeyPath: #keyPath(UINavigationItem.rightBarButtonItems), options: [.old, .new], context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == #keyPath(UINavigationItem.leftBarButtonItems) || keyPath == #keyPath(UINavigationItem.rightBarButtonItems) else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // 判断是否处于标记模式:检查是否存在「完成」按钮(适配多语言) let currentMode = navigationItem.leftBarButtonItems?.contains(where: { $0.title == NSLocalizedString("Done", comment: "") }) ?? false guard currentMode != isInMarkupMode else { return } isInMarkupMode = currentMode // 更新自定义按钮显示状态 customBarButtonItem.isHidden = isInMarkupMode } deinit { // 移除KVO监听 navigationItem.removeObserver(self, forKeyPath: #keyPath(UINavigationItem.leftBarButtonItems)) navigationItem.removeObserver(self, forKeyPath: #keyPath(UINavigationItem.rightBarButtonItems)) } }
注意:也可以通过按钮图片的描述特征(如包含「Markup」关键词)判断,避免语言依赖。
方法2:遍历子控制器检测Pencil Kit组件
标记模式的核心是Pencil Kit的PKCanvasView,它存在于QLPreviewController内部子控制器的视图层级中,可通过遍历视图树检测:
import PencilKit class CustomQLPreviewController: QLPreviewController { private let customBarButtonItem = UIBarButtonItem(title: "自定义", style: .plain, target: nil, action: nil) private var isInMarkupMode = false override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = customBarButtonItem } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let currentMode = checkForPKCanvasView() guard currentMode != isInMarkupMode else { return } isInMarkupMode = currentMode customBarButtonItem.isHidden = isInMarkupMode } // 递归遍历视图树,检查是否存在PKCanvasView private func checkForPKCanvasView() -> Bool { func scanView(_ view: UIView) -> Bool { if view is PKCanvasView { return true } for subview in view.subviews { if scanView(subview) { return true } } return false } // 遍历所有子控制器的视图 for childVC in childViewControllers { if scanView(childVC.view) { return true } } return false } }
此方法需确保项目已导入PencilKit框架。
方法3:监听标记按钮与完成按钮的点击
找到QLPreviewController原生的标记按钮,添加点击事件监听;同时监听完成按钮的点击,切换自定义按钮的显示状态:
class CustomQLPreviewController: QLPreviewController { private let customBarButtonItem = UIBarButtonItem(title: "自定义", style: .plain, target: nil, action: nil) override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 找到标记按钮并添加点击监听 let markupButton = navigationItem.rightBarButtonItems?.first { $0.image?.description.contains("Markup") == true } markupButton?.addTarget(self, action: #selector(enterMarkupMode), for: .touchUpInside) } @objc private func enterMarkupMode() { // 延迟更新,确保标记模式完全加载 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.customBarButtonItem.isHidden = true // 监听完成按钮 self.setupDoneButtonListener() } } private func setupDoneButtonListener() { let doneButton = navigationItem.leftBarButtonItems?.first { $0.title == NSLocalizedString("Done", comment: "") } doneButton?.addTarget(self, action: #selector(exitMarkupMode), for: .touchUpInside) } @objc private func exitMarkupMode() { customBarButtonItem.isHidden = false } }
此方法需注意按钮的动态变化,可在viewDidLayoutSubviews中重复执行按钮查找逻辑,确保监听生效。
内容的提问来源于stack exchange,提问作者jrodrigues
相关产品推荐
相关产品推荐

