iOS 13 UIActivityViewController 在图像保存后自动呈现以前的 VC
Posted
技术标签:
【中文标题】iOS 13 UIActivityViewController 在图像保存后自动呈现以前的 VC【英文标题】:iOS 13 UIActivityViewController automatically present previous VC after image saving 【发布时间】:2019-07-05 12:26:22 【问题描述】:我正在尝试实现“将图像保存到库”功能,然后返回到当前视图控制器,但在新的 ios 13 上,它会解散回呈现当前视图控制器的视图控制器:
phphotoLibrary.requestAuthorization((_ status: PHAuthorizationStatus) -> Void in )
let shareItems: Array = [newImg,"Hello"] as [Any]
let activityController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
if UIDevice.current.userInterfaceIdiom == .pad
activityController.popoverPresentationController?.sourceView = saveButton
present(activityController, animated: true)
【问题讨论】:
是的,我可以在 iOS 13 上确认同样的问题。我建议您提交错误报告。 有趣的是,我注意到该错误仅在您选择保存图像时才会出现。如果您选择其他选项,它似乎会返回到正确的视图控制器。 我遇到了同样的问题。这甚至发生在运行 iOS 13 且带有使用 Xcode 10 构建的应用的设备上。 也可以确认。这有什么好运气吗? 这是一个非常好的错误! 【参考方案1】:@KDP 解决方案的 Swift 版本:
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
要么被活动完成而解除,要么我们需要在完成时解除它。
【讨论】:
太好了,这完美无瑕,谢谢!顺便说一句,与 iOS 13 相同的问题在 iOS 14 上。【参考方案2】:我可以确认这个错误在 iOS 13.3.1 中仍然存在。以下解决方法是 franze's solution 的 Swift 版本。我更喜欢这种方法,因为它不对视图控制器层次结构做任何进一步的假设,也不使用方法调配。
使用这个额外的UIWindow
会破坏iOS 12 及更早版本UIActivityViewController
的取消 按钮,因此我添加了对操作系统版本的检查。
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)
更新:显然,此解决方案在 iPad 上无法可靠运行。看起来在 iPad 上UIActivityViewController
的呈现方式有所不同,一旦它在屏幕上可见,就不会注册任何触摸事件,从而有效地冻结了应用程序。
【讨论】:
谢谢!终于解决了我的情况。我只更改了这一行activityViewController.completionWithItemsHandler = (activity: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in )
经过进一步测试,我注意到我的原始解决方案在 iOS 12 及更早版本上无法正常工作 - 请查看我的更新答案。
@Theo 我假设这是在插件的 .m 文件中实现的,但我很难找到在哪里,我尝试绕过 getTopMostViewController 函数,但它只是破坏了它你可以显示你在哪里实现这会覆盖标准视图。提前谢谢你
@troggy69 在呈现UIActivityViewController
的视图控制器中实现该方法。然后,不要调用present(activityViewController, animated: true)
,而是调用showActivityViewController()
。
请注意我刚刚添加的警告 - 此解决方案不适用于 iPad。如果有人知道如何解决它,我会很感激编辑/cmets。【参考方案3】:
我生成了以下猴子补丁(由 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);
【讨论】:
【参考方案4】:这是我目前解决此错误的方法。我创建了一个假视图控制器并将其推送到当前堆栈。似乎 UIActivityTypeSaveToCameraRoll 取消了堆栈上的顶视图控制器,而其他选项则没有。如果活动类型不完整且 UIActivityTypeSaveToCameraRoll,我会在完成块中关闭假视图控制器。
typeof(self) __weak weakSelf = self;
[self.activityViewController setCompletionHandler:^(NSString *activityType, BOOL completed)
if (activityType== UIActivityTypeSaveToCameraRoll && completed)
weakSelf.activityViewController = nil;
else
[weakSelf dismissViewControllerAnimated:NO completion:nil];
weakSelf.activityViewController = nil;
];
UIViewController *fakeVC=[[UIViewController alloc] init];
[self presentViewController:fakeVC animated:NO completion:^
[fakeVC presentViewController:self.activityViewController animated:YES completion:nil];
];
【讨论】:
【参考方案5】:- (UIWindow *)displayWindow
if (!_displayWindow)
_displayWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
_displayWindow.rootViewController = [[UIViewController alloc] init];
return _displayWindow;
- (void)showActivityController
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[] applicationActivities:nil];
activityViewController.completionWithItemsHandler = ^(UIActivityType __nullable activityType, BOOL completed, NSArray * __nullable returnedItems, NSError * __nullable activityError)
[UIApplication.sharedApplication.delegate.window makeKeyAndVisible];
;
[self.displayWindow makeKeyAndVisible];
[self.displayWindow.rootViewController presentViewController:activityViewController animated:true completion:nil];
确保_displayWindow
是强引用。
【讨论】:
我实现了一个等效的解决方案,到目前为止效果很好。【参考方案6】:我通过将根视图控制器设置为当前窗口解决了这个问题,我不知道为什么它会关闭当前视图控制器。我注意到在 iOS 13 中呈现新的视图控制器时,它将呈现为卡片堆叠样式,如果我在 UIActivityController 中选择“保存图像”,则当前视图控制器(卡片)将被关闭并显示以前的视图控制器。
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window else return
window.rootViewController = viewcontroller
当我需要开始一个新故事时,我使用这个而不是呈现视图控制器。
顺便说一句,这取决于您的要求。在这种情况下,我可以忽略旧的未使用的,因为不需要返回,也许您可以使用它而不是呈现新场景。
【讨论】:
【参考方案7】:似乎它已在 iOS 14 中修复。对于旧版本,我发现通过使用空实现覆盖 dismiss(animated:)
方法更容易。见https://***.com/a/66595125/2095408
【讨论】:
【参考方案8】:对于那些在 iPad 上出现屏幕冻结的用户,这里有一个简单的解决方案。它也适用于 iPhone。
func shareItems(_ sharedItems: [Any])
let activityViewController = UIActivityViewController(activityItems: sharedItems, applicationActivities: nil)
if let popoverController = activityViewController.popoverPresentationController
popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
popoverController.sourceView = self.view
popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
self.present(activityViewController, animated: true, completion: nil)
【讨论】:
以上是关于iOS 13 UIActivityViewController 在图像保存后自动呈现以前的 VC的主要内容,如果未能解决你的问题,请参考以下文章
如何更改 UIActivityViewController 中的文本颜色和图标颜色