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

如何为QLPreviewController添加取消按钮(避免嵌套导航控制器)

QLPreviewController模态弹出时合规添加取消按钮方案

问题背景

我希望在应用内使用QLPreviewController实现图片编辑功能,采用如下原生代码模态弹出该控制器:

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    collectionView.deselectItem(at: indexPath, animated: true)
    let quickLookViewController = QLPreviewController()
    quickLookViewController.dataSource = self
    quickLookViewController.delegate = self
    tappedCell = collectionView.cellForItem(at: indexPath) as? FileCell
    quickLookViewController.currentPreviewItemIndex = indexPath.row
    present(quickLookViewController, animated: true)
}

运行实现效果如下图所示:
QLPreviewController运行效果

核心诉求

  • 不接受将QLPreviewController嵌入UINavigationController的hack方案,该方案会引发转场动画异常。
  • 需要符合Apple开发规范的方案,为控制器添加取消按钮。
  • 期望交互和iOS系统自带照片应用一致:标注操作时同时提供「完成」和「取消」按钮。

合规实现方案

完全不需要嵌套导航控制器、不需要访问私有API,仅通过UIKit公开能力即可实现,不会破坏原生转场和编辑逻辑:

  1. 子类化QLPreviewController,在正确的生命周期节点设置导航栏取消按钮,避免按钮被系统内部逻辑覆盖:
class EditablePreviewController: QLPreviewController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // viewWillAppear时机设置不会被系统重置
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .cancel,
            target: self,
            action: #selector(handleCancelTap)
        )
    }

    @objc private func handleCancelTap() {
        // 可在此处添加未保存编辑的提示逻辑
        dismiss(animated: true)
    }
}
  1. 替换原有弹出逻辑中的控制器类为自定义子类,其余代理、索引配置逻辑完全保持不变即可。

实现说明

  • 禁止在viewDidLoad中设置导航按钮:QLPreviewController内部会在viewDidLoad执行完成后重置自身navigationItem,此时添加的按钮会被系统覆盖无法显示。
  • 该方案仅修改控制器公开的navigationItem属性,属于Apple文档明确允许的自定义行为,不会触发私有API审核风险,也不会干扰系统原生的编辑、转场交互。
  • 适配低版本系统时仅需要将控制器的modalPresentationStyle设置为.fullScreen,按钮逻辑可兼容所有支持QLPreviewController编辑功能的系统版本。

内容的提问来源于stack exchange,提问作者Kex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 01:15:40