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

如何识别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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 22:37:04