Неправильный фрейм при отклонении модально представленного контроллера представления

Я представляю UIViewController с использованием настраиваемого перехода и настраиваемого UIPresentationController. Представление контроллера представления не покрывает весь экран, поэтому контроллер представления представления все еще виден.

Затем я представляю экземпляр UIImagePickerController поверх этого контроллера представления. Проблема в том, что когда я закрываю средство выбора изображений, рамка контроллера представления представления покрывает весь экран, а не только ту часть, которую я хочу, чтобы она покрывала. Фрейм, указанный frameOfPresentedViewInContainerView в моем пользовательском UIPresentationController, кажется, полностью игнорируется.

Только если в средстве выбора изображений присутствует modalPresentationStyle из UIModalPresentationOverCurrentContext, мои фреймы остаются нетронутыми (что имеет смысл, поскольку представления не удаляются из иерархии представлений в первую очередь). К сожалению, я не этого хочу. Я хочу, чтобы средство выбора изображений было представлено в полноэкранном режиме, что по какой-то причине, похоже, испортило мой макет. Что-нибудь, что я здесь делаю неправильно или забываю? Какие-либо предложения?


comment
имеют аналогичную проблему, но с UIActivityViewController и только при использовании полноэкранного совместного использования, такого как Сообщения или Почта   -  person race_carr    schedule 03.02.2017
comment
Для меня неправильный полноэкранный фрейм при отклонении модально представленного контроллера представления появляется на мгновение до появления правильного представления.   -  person sandpat    schedule 18.09.2018


Ответы (5)


Я пробовал оба упомянутых подхода к оболочке. Одним из побочных эффектов использования этого подхода к упаковке является то, что вращение устройства не отображается должным образом - вокруг представленного представления появляются черные прямоугольники.

Вместо того, чтобы делать трюк с упаковкой, попробуйте установить modalPresentationStyle представленного UIImagePickerController на UIModalPresentationOverFullScreen. Это означает, что представления под средством выбора изображений не будут удалены / восстановлены из иерархии представлений во время презентации / закрытия.

person Thomas Verbeek    schedule 22.01.2017
comment
Это намного лучше - гораздо меньше взлома и более гарантированно работает в долгосрочной перспективе. - person Cal Stephens; 27.08.2017
comment
Святая корова .... Я бился головой о каждую твердую поверхность, пытаясь понять это. Никогда не мог себе представить, что это такое простое решение. Огромное спасибо ! :) Кстати, это должен быть принятый ответ. - person AnBisw; 05.01.2018
comment
Это серьезно спасло мою задницу. Возникла проблема, связанная только с iPhone X. Спасибо, чувак! - person G_Money; 14.03.2018
comment
Это абсолютно работает, когда у вас есть контроль над контроллером, который вы представляете, например UIImagePickerViewController. Но когда вы представляете UIActivityViewController, а затем пользователь выбирает что-то вроде Сохранить в файлы или сообщения, UIActivityViewController отклоняется, а затем отображается что-то еще (например, композитор сообщений), и в этой ситуации вам не предоставляется обратный вызов для установки modalPresentationStyle. Таким образом, та же ошибка возникает, когда пользователь заканчивает сохранение в файлы или отправку сообщения. Есть какие-нибудь мысли о применении этого подхода в этой ситуации? - person UberJason; 09.08.2018
comment
при отклонении модально представленного контроллера представления с этим подходом представленный контроллер представления UIPresentationController теперь остается в полноэкранном режиме :( - person sandpat; 18.09.2018

Это ожидается, потому что полноэкранное представление не восстанавливает исходный кадр, вычисленный frameOfPresentedViewInContainerView. Рекомендуемый способ исправить это - создать представление оболочки, в которое вы вставите представленное представление контроллера представления. Вот соответствующий код для вашего настраиваемого контроллера представления:

- (void)presentationTransitionWillBegin {
    // wrapper is a property defined in the custom presentation controller.
    self.wrapper = [UIView new];
    [self.wrapper addSubview:self.presentedViewController.view];
}

- (CGRect)frameOfPresentedViewInContainerView {
    CGRect result = self.containerView.frame;

    // In this example we are doing a half-modal presentation
    CGFloat height = result.size.height/2;
    result.origin.y = height;
    result.size.height = height;

    return result;
}

- (UIView *)presentedView {
    return self.wrapper;
}

- (BOOL)shouldPresentInFullscreen {
    return NO;
}

- (void)containerViewWillLayoutSubviews {
    self.wrapper.frame = self.containerView.frame;
    self.presentedViewController.view.frame = [self frameOfPresentedViewInContainerView];
}

Обратите внимание, что мы переопределяем presentedView, чтобы вернуть представление оболочки вместо значения по умолчанию - представленного представления контроллера представления. Таким образом, даже если вторая презентация изменяет фрейм оболочки, представление представленного контроллера представления не изменится.

person erudel    schedule 23.06.2016
comment
Спасибо за предложение @erudel, но, к сожалению, это не решает мою проблему. Из этого я получаю, что настраиваемый представленный контроллер представления является правильным после анимации отклонения представленного им контроллера представления, что является эффектом, которого я уже достиг ранее, просто перенастроив кадр представил представление контроллера представления в containerViewWillLayoutSubviews (нет необходимости в представлении оболочки). Во время анимации закрытия, однако, кадр представления остается в полноэкранном режиме, что приводит к скачкообразному изменению макета и, конечно же, изначально неверно. - person caryot; 23.06.2016
comment
containerViewWillLayoutSubviews обрабатывает связанные изменения, такие как поворот или адаптация, но представление оболочки необходимо именно для того, чтобы избежать этого перехода при закрытии полноэкранного представления. - person erudel; 23.06.2016
comment
Дело в том, что я реализовал это именно так, как вы предлагали, но все равно получаю jump @erudel. - person caryot; 23.06.2016
comment
Вы можете поделиться своим кодом? Я написал образец тестового приложения с приведенным выше кодом, и оно работает правильно. - person erudel; 24.06.2016
comment
Работай на меня! Отлично! - person vitkuzmenko; 21.06.2017

решение erudel не сработало для меня как есть, но добавление другого представления между wrapperView и presentedViewController.view помогло (у меня не знаю почему):

- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController
                       presentingViewController:(UIViewController *)presentingViewController {
    if (self = [super initWithPresentedViewController:presentedViewController
                             presentingViewController:presentingViewController]) {
        _wrapperView = [[UIView alloc] init];
        _wrapperView2 = [[UIView alloc] init]; // <- new view
    }
    return self;
}

- (CGRect)frameOfPresentedViewInContainerView {
    return self.containerView.bounds;
}

- (UIView *)presentedView {
    return self.wrapperView;
}

- (BOOL)shouldPresentInFullscreen {
    return NO;
}

- (void)containerViewWillLayoutSubviews {
    self.wrapperView.frame = self.containerView.frame;
    self.wrapperView2.frame = /* your custom frame goes here */;
    self.presentedViewController.view.frame = self.wrapperView2.bounds;
}

- (void)presentationTransitionWillBegin {
    [self.wrapperView addSubview:self.wrapperView2];
    [self.wrapperView2 addSubview:self.presentedViewController.view];

    // Set up a dimming view, etc
}

Проверено на iOS 9.3.

person iosdude    schedule 08.08.2016

Это сработало для меня в UIPresentationController:

override func containerViewWillLayoutSubviews() {

     super.containerViewWillLayoutSubviews()
     presentedViewController.view.frame = frameOfPresentedViewInContainerView
}
person sash    schedule 30.05.2017
comment
Это мне очень помогло! Спасибо! - person aleksie3006; 14.08.2020

Я попробовал containerViewWillLayoutSubviews, но это не совсем сработало. Я хотел по возможности избежать дополнительных просмотров оболочки. Я придумал эту стратегию исправления кадра в представлении с помощью presentedView. Вдобавок мне удалось удалить containerViewWillLayoutSubviews. forceFrame настроен на наш конкретный вариант использования. presentedFrame устанавливается нашим пользовательским аниматором.

class CustomModalPresentationController: UIPresentationController {

        var presentedFrame = CGRect.zero
        var forceFrame = false

        override func dismissalTransitionWillBegin() {
            forceFrame = false
        }
        override func presentationTransitionDidEnd(_ completed: Bool) {
            forceFrame = true
        }
        override var presentedView: UIView? {
            if forceFrame {
                presentedViewController.view.frame = presentedFrame
            }
            return presentedViewController.view
        }
        override var frameOfPresentedViewInContainerView: CGRect {
            return presentedFrame
        }
    }
person SuperGuyAbe    schedule 04.11.2017
comment
Этот исправил это для меня. По причинам, о которых я не могу беспокоиться, изменение modalPresentationStyle на UIModalPresentationOverFullScreen было бы неплохо, но на самом деле это было вне моего контроля без более масштабного рефакторинга (использование внешней библиотеки). Обертки казались неуклюжими. Но это исправило это для меня! Спасибо! - person Iain Stanford; 10.06.2020