iosipaduikituipopovercontrolleripados

iOS 17 UIKit Popover Crash on iPad - UIPopoverPresentationController should have a non-nil sourceView


On iPadOS 17, a crash occurs when dismissing a popover and then quickly presenting one again. Usually the error is the following:

UIPopoverPresentationController should have a non-nil sourceView or barButtonItem set before the presentation occurs

However I've occasionally seen the following:

Application tried to present modally a view controller that is already being presented by the [main view controller]

This is the minimal source code necessary to reproduce this crash:

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)
    }
}

Some additional things to consider about this issue:

  1. This does not occur on iOS 16 or earlier.
  2. Setting the .sourceRect on the popoverPresentationController has no effect.
  3. Setting the frame or preferredContentSize of the popover has no effect.
  4. The crash occurs in UIKit with or without Storyboards (hard-coded UI).
  5. The crash does not occur in SwiftUI. Please don't suggest SwiftUI as a fix.
  6. I can add a guard before presenting to make sure the sourceView is not nil but it has no effect.

This issue seems to occur because the popover view controller isn't being fully de-allocated from memory when a new popover is being presented. This is creating some sort of collision. I've had success swizzling UIPopoverPresentationController.dismissalTransitionWillBegin and I'd be happy to post that solution, but first I'd like to see if there are less hacky workarounds. I do not want to simply override dismissalTransitionWillBegin because I want the default behaviour and would rather not rewrite my own popover animations just to satisfy this bug.

enter image description here


Solution

  • I get the same crash on iPadOS 17 too. It doesn't happen all the time so it's tricky why this happens.

    My current workaround is to guard check if the current view controller is presenting another view controller, before going ahead and present a new one. So far this seems to work for me.

    guard self.presentedViewController == nil else { return }