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

iPadOS 17 UIKit Popover重复快速呈现崩溃问题求助

iPadOS 17 快速重复呈现Popover崩溃的非侵入式解决方案

在iPadOS 17系统中,关闭Popover后快速触发再次呈现操作时会出现崩溃,常见错误提示有两种:

  • UIPopoverPresentationController should have a non-nil sourceView or barButtonItem set before the presentation occurs
  • Application tried to present modally a view controller that is already being presented by the [main view controller]

可复现代码

class ViewController: UIViewController {

    let popoverVC = UIViewController()
    
    @IBOutlet var showPopoverButton: UIButton!
    
    @IBAction func showPopoverTapped(_ sender: UIButton) {
        popoverVC.modalPresentationStyle = .popover
        popoverVC.popoverPresentationController?.sourceView = showPopoverButton
        present(popoverVC, animated: true)
    }
}

问题补充说明

  • 仅iPadOS 17存在此问题,iOS 16及更早版本无异常;
  • 为popoverPresentationController设置.sourceRect无法解决崩溃;
  • 修改Popover的frame或preferredContentSize无效;
  • 无论使用Storyboard还是纯代码编写UI,UIKit中都会触发崩溃;
  • SwiftUI不存在此问题,但请勿推荐SwiftUI作为解决方案;
  • 呈现前添加guard校验sourceView非空无效果。

问题原因推测

崩溃源于Popover视图控制器在dismissal动画未完成、内存未完全释放时被重复触发呈现,导致视图控制器状态冲突、PresentationController参数异常。

非侵入式解决方案

方案1:避免复用同一个Popover实例

每次触发呈现时创建新的Popover实例,从根源上避免状态冲突:

class ViewController: UIViewController {
    
    @IBOutlet var showPopoverButton: UIButton!
    
    @IBAction func showPopoverTapped(_ sender: UIButton) {
        // 每次点击创建新的Popover实例
        let popoverVC = UIViewController()
        popoverVC.modalPresentationStyle = .popover
        if let popoverPC = popoverVC.popoverPresentationController {
            popoverPC.sourceView = sender
            popoverPC.sourceRect = sender.bounds // 可选,优化弹出位置精度
        }
        present(popoverVC, animated: true)
    }
}

方案2:复用实例时确保状态正确

若需复用同一个Popover实例,需先检查其是否处于被呈现状态,等待dismiss完成后再触发呈现:

class ViewController: UIViewController {

    let popoverVC = UIViewController()
    
    @IBOutlet var showPopoverButton: UIButton!
    
    @IBAction func showPopoverTapped(_ sender: UIButton) {
        // 检查Popover是否正在被呈现
        guard popoverVC.presentingViewController == nil else {
            // 先关闭当前Popover,完成后再重新呈现
            popoverVC.dismiss(animated: false) { [weak self] in
                self?.presentPopover(from: sender)
            }
            return
        }
        presentPopover(from: sender)
    }
    
    private func presentPopover(from sender: UIButton) {
        popoverVC.modalPresentationStyle = .popover
        popoverVC.popoverPresentationController?.sourceView = sender
        present(popoverVC, animated: true)
    }
}

方案说明

两种方案均无需使用Method Swizzling或重写动画逻辑,属于UIKit规范内的非hacky解决方式:

  • 方案1实现简单,适合Popover内容无需复用状态的场景;
  • 方案2保留实例复用,适合需要维持Popover内部状态的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 19:53:40