IOS 13 UIActivityViewController автоматически представляет предыдущий VC после сохранения изображения

Я пытаюсь реализовать функцию «Сохранить изображение в библиотеке», а затем вернуться к текущему контроллеру представления, но в новой 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. Я предлагаю вам отправить отчет об ошибке.

Rob 05.07.2019 16:20

Не имеет отношения, но нет причин для проверки устройства. Вы можете установить sourceView независимо. Но вы также должны установить sourceRect.

rmaddy 05.07.2019 16:39

Интересно, я заметил, что ошибка проявляется только в том случае, если вы выбираете сохранение изображения. Если вы выберете другие параметры, он вернется к правильному контроллеру представления.

Rob 05.07.2019 18:22

Я вижу ту же проблему. Это происходит даже на устройстве под управлением iOS 13 с приложением, созданным с помощью Xcode 10.

bdmontz 23.09.2019 18:39

Тоже могу подтвердить. Удачи с этим?

Diarrhio 01.10.2019 13:00

То же самое. Xcode 10.1, работающий на iOS 13.0. Видел эту ошибку более чем на одном сайте, но пока нет идей, как ее исправить.

Daniel Martinez Condinanza 01.10.2019 16:29

То же самое. Xcode 11.1 работает на iOS 13.1.3. Нет успеха с любым из решений ниже.

Marta Rodriguez 28.10.2019 11:22

Это действительно хороший баг!

matt 14.11.2019 05:29

Кажется исправлено в iOS 14.4

Palli 12.03.2021 07:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
33
9
8 940
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Я решил эту проблему, установив контроллер корневого представления в текущее окно, я понятия не имею, почему он отклоняет текущий контроллер представления. Я заметил, что при представлении нового контроллера представления в iOS 13 он будет представлять стиль стека карт, и если я выберу «Сохранить изображение» в UIActivityController, то текущий контроллер представления (карта) будет отклонен и отобразится предыдущий контроллер представления.

guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
      let window = appDelegate.window else { return }
window.rootViewController = viewcontroller

Я использую его вместо представления контроллера представления, когда мне нужно начать новую историю.

Кстати, это зависит от ваших требований. В этом случае я могу отклонить старую неиспользованную сцену, потому что не нужно ее поддерживать, может быть, вы можете использовать ее вместо представления новой сцены.

Вот как я работал над этой ошибкой на данный момент. Я создаю поддельный контроллер представления и помещаю его в текущий стек. Кажется, что 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];
}];

Я сгенерировал следующий патч для обезьян (проверено 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);
}
- (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 является сильной ссылкой.

Я реализовал эквивалентное решение, и до сих пор оно работало хорошо.

Drew 02.01.2020 18:46

Быстрая версия решения @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 либо отклоняется по завершению действия, либо нам нужно отклонить его по завершению.

Отлично, это работает безупречно, спасибо!. Та же проблема, что и в iOS 13, есть и в iOS 14.

sabiland 02.11.2020 09:05
Ответ принят как подходящий

Я могу подтвердить, что эта ошибка все еще присутствует в iOS 13.3.1. Следующий обходной путь — это Swift-версия раствор Франце. Я предпочитаю этот подход, так как он не делает никаких дополнительных предположений об иерархии контроллера представления и не использует метод swizzling.

Использование этого дополнительного UIWindow ломает кнопку ОтменаUIActivityViewController на iOS 12 и более ранних версиях, поэтому я добавил проверку версии ОС.

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

Diana 05.04.2020 00:07

При дальнейшем тестировании я заметил, что мое исходное решение некорректно работает на iOS 12 и более ранних версиях — см. мой обновленный ответ.

Theo 05.04.2020 10:54

@Theo Я предполагаю, что это реализовано в файле .m плагина, но я изо всех сил пытаюсь найти, где, я пытался обойти функцию getTopMostViewController, но она просто ломается, не могли бы вы показать, где вы реализовали это, чтобы перезаписать стандартный вид. заранее спасибо

troggy69 07.05.2020 15:40

@troggy69 Реализуйте метод в контроллере представления, который представляет UIActivityViewController. Затем, вместо того, чтобы звонить present(activityViewController, animated: true), звоните showActivityViewController().

Theo 07.05.2020 17:19

Обратите внимание на мое предупреждение, которое я только что добавил — это решение не работает на iPad. Если кто-то знает, как это исправить, буду признателен за правки/комментарии.

Theo 07.05.2020 17:32

@Theo не уверен, поможет ли это вам, но проблема, которую я обнаружил с ipad, заключается в том, что он отображается за представлением, поэтому при открытии вам нужно добавить (в зависимости от ваших переменных) ref.show(); на моем мне пришлось сначала добавить ref.hide(). я сомневаюсь, что это будет то, что вам нужно, но может пролить некоторое понимание

troggy69 07.05.2020 17:55

Также, что касается моего вопроса реализации, я прошу прощения, так как я очень плохо сформулировал его, мое приложение - это приложение Cordova, и я пытаюсь внедрить ваше исправление в cordova-plugin-file-opener2 в файл .m в папке ios, я считаю, что стиль кода является объективным c из что я могу сказать, так как я очень давно не программировал приложения

troggy69 07.05.2020 17:59

это не работает - приложение зависает после закрытия контроллера активности

Vyachaslav Gerchicov 23.06.2020 15:03

Обновление: для iOS 14 — majorVersion == 14

Diana 30.06.2020 22:49

Это решение вызывает некоторые проблемы в моем случае. Это ведет себя странно после увольнения и представления в следующий раз.

Mehdi Gilanpour 07.07.2020 09:16

Я могу подтвердить, что эта ошибка все еще присутствует в iOS 14 beta 3. Я отправил отчет об ошибке FB8111145.

Theo 23.07.2020 10:28

Для тех, у кого зависает экран на iPad, вот простое решение. На айфоне тоже работает.

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 14. Для более старых версий я нашел более простой обходной путь с помощью переопределения метода dismiss(animated:) с пустой реализацией. См. https://stackoverflow.com/a/66595125/2095408

Другие вопросы по теме