Я пытаюсь реализовать функцию «Сохранить изображение в библиотеке», а затем вернуться к текущему контроллеру представления, но в новой 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)
Не имеет отношения, но нет причин для проверки устройства. Вы можете установить sourceView независимо. Но вы также должны установить sourceRect.
Интересно, я заметил, что ошибка проявляется только в том случае, если вы выбираете сохранение изображения. Если вы выберете другие параметры, он вернется к правильному контроллеру представления.
Я вижу ту же проблему. Это происходит даже на устройстве под управлением iOS 13 с приложением, созданным с помощью Xcode 10.
Тоже могу подтвердить. Удачи с этим?
То же самое. Xcode 10.1, работающий на iOS 13.0. Видел эту ошибку более чем на одном сайте, но пока нет идей, как ее исправить.
То же самое. Xcode 11.1 работает на iOS 13.1.3. Нет успеха с любым из решений ниже.
Это действительно хороший баг!
Кажется исправлено в iOS 14.4





Я решил эту проблему, установив контроллер корневого представления в текущее окно, я понятия не имею, почему он отклоняет текущий контроллер представления. Я заметил, что при представлении нового контроллера представления в 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 является сильной ссылкой.
Я реализовал эквивалентное решение, и до сих пор оно работало хорошо.
Быстрая версия решения @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.
Я могу подтвердить, что эта ошибка все еще присутствует в 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 )}
При дальнейшем тестировании я заметил, что мое исходное решение некорректно работает на iOS 12 и более ранних версиях — см. мой обновленный ответ.
@Theo Я предполагаю, что это реализовано в файле .m плагина, но я изо всех сил пытаюсь найти, где, я пытался обойти функцию getTopMostViewController, но она просто ломается, не могли бы вы показать, где вы реализовали это, чтобы перезаписать стандартный вид. заранее спасибо
@troggy69 Реализуйте метод в контроллере представления, который представляет UIActivityViewController. Затем, вместо того, чтобы звонить present(activityViewController, animated: true), звоните showActivityViewController().
Обратите внимание на мое предупреждение, которое я только что добавил — это решение не работает на iPad. Если кто-то знает, как это исправить, буду признателен за правки/комментарии.
@Theo не уверен, поможет ли это вам, но проблема, которую я обнаружил с ipad, заключается в том, что он отображается за представлением, поэтому при открытии вам нужно добавить (в зависимости от ваших переменных) ref.show(); на моем мне пришлось сначала добавить ref.hide(). я сомневаюсь, что это будет то, что вам нужно, но может пролить некоторое понимание
Также, что касается моего вопроса реализации, я прошу прощения, так как я очень плохо сформулировал его, мое приложение - это приложение Cordova, и я пытаюсь внедрить ваше исправление в cordova-plugin-file-opener2 в файл .m в папке ios, я считаю, что стиль кода является объективным c из что я могу сказать, так как я очень давно не программировал приложения
это не работает - приложение зависает после закрытия контроллера активности
Обновление: для iOS 14 — majorVersion == 14
Это решение вызывает некоторые проблемы в моем случае. Это ведет себя странно после увольнения и представления в следующий раз.
Я могу подтвердить, что эта ошибка все еще присутствует в iOS 14 beta 3. Я отправил отчет об ошибке FB8111145.
Для тех, у кого зависает экран на 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
Да, я могу подтвердить ту же проблему на iOS 13. Я предлагаю вам отправить отчет об ошибке.