iOS 13 UIActivityViewController automatically present previous VC after image saving
Swift version of @KDP's solution:
let fakeViewController = TransparentViewController()
fakeViewController.modalPresentationStyle = .overFullScreen
activityViewController.completionWithItemsHandler = { [weak fakeViewController] _, _, _, _ in
if let presentingViewController = fakeViewController?.presentingViewController {
presentingViewController.dismiss(animated: false, completion: nil)
} else {
fakeViewController?.dismiss(animated: false, completion: nil)
}
}
present(fakeViewController, animated: true) { [weak fakeViewController] in
fakeViewController?.present(activityViewController, animated: true, completion: nil)
}
fakeViewController
gets either dismissed by activity completion, or we need to dismiss it in completion.
I can confirm that this bug is still present in iOS 13.3.1. The following workaround is a Swift version of franze's solution. I prefer this approach, as it doesn't make any further assumptions on the view controller hierarchy and doesn't use method swizzling.
Using this additional UIWindow
breaks the Cancel button of the UIActivityViewController
on iOS 12 and earlier, so I added a check for the OS version.
private let activityWindow: UIWindow = {
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIViewController()
return window
}()
func showActivityController() {
let activityViewController = UIActivityViewController(/* ... */)
activityViewController.completionWithItemsHandler = {
// ...
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
}
// Use this workaround only on iOS 13
if ProcessInfo.processInfo.operatingSystemVersion.majorVersion == 13 {
activityWindow.makeKeyAndVisible()
activityWindow.rootViewController?.present(activityViewController, animated: true)
} else {
present(activityViewController, animated: true)
}
}
Update: Apparently, this solution doesn't work reliably on iPads. It looks like on iPad the UIActivityViewController
is presented differently and as soon as it's visible on screen, no touch events are registered, effectively freezing the app.
I generated following monkey patch (Checked by iOS 13.1.2)
- (void)export {
//
// ... Generate Your Activity Items ...
//
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems
applicationActivities:nil
completeBlock:^(NSError *activityError, BOOL completed) {
// Swizzling Dismiss Method
[[self class] switchInstanceMethodFrom:@selector(dismissViewControllerAnimated:completion:) To:@selector(lockedDismissViewControllerAnimated:completion:)];
}
];
// Swizzling Dismiss Method
[[self class] switchInstanceMethodFrom:@selector(dismissViewControllerAnimated:completion:) To:@selector(lockedDismissViewControllerAnimated:completion:)];
[self presentViewController:activityViewController animated:YES completion:nil];
}
- (void)lockedDismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
if ([self presentedViewController]) {
[self lockedDismissViewControllerAnimated:flag completion:completion];
}
}
// from http://qiita.com/paming/items/25eaf89e4f448ab05752
+(void)switchInstanceMethodFrom:(SEL)from To:(SEL)to
{
Method fromMethod = class_getInstanceMethod(self,from);
Method toMethod = class_getInstanceMethod(self,to );
method_exchangeImplementations(fromMethod, toMethod);
}