iOS 13 UIActivityViewController在保存图像后自动显示以前的VC

时间:2019-07-05 12:26:22

标签: ios swift uiactivityviewcontroller ios13

我正在尝试实现“将图像保存到库”功能,然后返回到当前的视图控制器,但是在新的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)

8 个答案:

答案 0 :(得分:5)

我可以确认此错误仍在iOS 13.3.1中存在。以下解决方法是franze's solution的Swift版本。我更喜欢这种方法,因为它不会在视图控制器层次结构上做任何进一步的假设,并且不会使用方法混乱。

使用此附加UIWindow会中断iOS 12及更低版本上UIActivityViewController Cancel 按钮,因此我添加了对OS版本的检查。

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的显示方式似乎有所不同,并且只要在屏幕上可见,就不会记录任何触摸事件,从而有效地冻结了该应用程序。

答案 1 :(得分:4)

我生成了以下猴子补丁(由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);
}

答案 2 :(得分:3)

@KDP解决方案的快速版本:

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要么由于活动完成而被撤消,要么我们需要在完成时将其撤消。

答案 3 :(得分:1)

这是我现在解决此错误的方法。我创建了一个伪造的视图控制器,并将其推入当前堆栈。看来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];
}];

答案 4 :(得分:1)

- (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是强引用。

答案 5 :(得分:0)

我通过将根视图控制器设置为当前窗口来解决此问题,我不知道为什么它会关闭当前视图控制器。我注意到,在iOS 13中展示新的视图控制器时,它将呈现为卡片堆叠样式,如果我在UIActivityController中选择了“保存图像”,则当前的视图控制器(卡片)将被关闭并显示以前的视图控制器。

答案 6 :(得分:0)

对于那些在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)
}

答案 7 :(得分:0)

似乎它已在 iOS 14 中修复。对于旧版本,我发现通过使用空实现覆盖 dismiss(animated:) 方法更容易解决。见https://stackoverflow.com/a/66595125/2095408