如何为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嵌入UINavigationController的hack方案,该方案会引发转场动画异常。 - 需要符合Apple开发规范的方案,为控制器添加取消按钮。
- 期望交互和iOS系统自带照片应用一致:标注操作时同时提供「完成」和「取消」按钮。
合规实现方案
完全不需要嵌套导航控制器、不需要访问私有API,仅通过UIKit公开能力即可实现,不会破坏原生转场和编辑逻辑:
- 子类化
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) } }
- 替换原有弹出逻辑中的控制器类为自定义子类,其余代理、索引配置逻辑完全保持不变即可。
实现说明
- 禁止在
viewDidLoad中设置导航按钮:QLPreviewController内部会在viewDidLoad执行完成后重置自身navigationItem,此时添加的按钮会被系统覆盖无法显示。 - 该方案仅修改控制器公开的
navigationItem属性,属于Apple文档明确允许的自定义行为,不会触发私有API审核风险,也不会干扰系统原生的编辑、转场交互。 - 适配低版本系统时仅需要将控制器的
modalPresentationStyle设置为.fullScreen,按钮逻辑可兼容所有支持QLPreviewController编辑功能的系统版本。
内容的提问来源于stack exchange,提问作者Kex
相关产品推荐
相关产品推荐

