Search code examples
iosswiftuiactivityviewcontroller

Why does UIActivityViewController call viewWillDisappear() on the presenter?


I am presenting a UIActivityViewController to share an .mp4 video from a URL:

let viewController: UIViewController = ... // the presenting view controller
let url: URL = ... // local file

let activityController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
viewController.present(activityController, animated: false, completion: nil)

When the "save video" option is selected, the video is saved, but the presenting UIViewController disappears (and I can see that .viewWillDisappear() is called on it.)

How do I make the presenting UIViewController not disappear?

Note that all of the other share options that I've tried do not have this problem (messages, airdrop, instagram).

I have tried to set the sourceView and sourceRect, but it does not seem to help.

activityController.popoverPresentationController?.sourceView = viewController.view!
activityController.popoverPresentationController?.sourceRect = viewController.view!.frame

I've looked for errors, but didn't find any:

activityController.completionWithItemsHandler = { (a: UIActivity.ActivityType?, b: Bool, c: [Any]?, d: Error?) in
    if let error = d {
        print(error)
    }
}

Also, all of my UIViewController lifecycle overrides call their super, i.e.:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
}

This is what it looks like: a disappearing view

It's bringing down my entire view with it!

For what it's worth, viewController is setup by calling a segue that is setup in a storyboard:

class LaunchController : UIViewController {

    var performedSegue = false

    override func viewDidLayoutSubviews() {
        if !performedSegue {
            self.performSegue(withIdentifier: "main", sender: self)
            performedSegue = true
        }
    }
}

Solution

  • It looks like this "feature" was introduced by Apple in iOS 13. I tested this on iOS 12 and the presenting ViewController does not disappear.

    I traced the call stack and it appear that UIActivityViewController is calling dismiss on presenting view controller (or it's UINavigationController) when saving to camera roll succeeds.

    enter image description here

    I don't know the way to prevent this since it's Apple private API and there's nothing in documentation about this. The only way I found, is to to set some kind of flag savingToCameraRoll and set it to true when presenting UIActivityViewController, override dismiss method on presenting ViewController and then check this flag inside dismiss.

    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
        if !savingToCameraRoll {
             // Dismiss view controller if UIActivityViewController not in use
             super.dismiss(animated: animated, completion: completion)
        }
    
        // Handle UIActivityViewController dismiss attempt. 
        // If you do nothing, the presenting ViewController should not be dismissed.
    }
    

    You should also remember to set savingToCameraRoll to false in completionWithItemsHandler.