Представление модального в iOS 13 в полноэкранном режиме


467

В iOS 13 есть новое поведение для модального контроллера представления, когда представлено.

Теперь это не полноэкранный режим по умолчанию, и когда я пытаюсь скользить вниз, приложение просто автоматически отключает View Controller.

Как я могу предотвратить это поведение и вернуться к старому полноэкранному модальному vc?

модальное поведение

Спасибо

Ответы:


623

В iOS 13, как было указано в разделе « Состояние платформ Союза во время WWDC 2019», Apple представила новую презентацию карт по умолчанию. Чтобы включить полноэкранный режим, вы должны явно указать его с помощью:

let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen //or .overFullScreen for transparency
self.present(vc, animated: true, completion: nil)

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

17
Я не буду рассчитывать на это. В прошлом Apple часто меняла настройки по умолчанию, становясь активными только после того, как вы установили связь с текущим SDK. Мы надеемся получить старое поведение при связывании с предыдущими версиями.
DrMickeyLauer

11
Я могу подтвердить, что встроенные приложения Xcode-10, запущенные на симуляторе iOS 13, по-прежнему работают в полноэкранном режиме. Как сказал @DrMickeyLauer, сборка с Xcode 11 переключает приложение на новое поведение. Используйте, isModalInPresentationчтобы заблокировать жест смахивания от отклонения. Смотрите мой блог для более подробной информации: medium.com/@hacknicity/…
Джефф Хэкворт,

5
Я рекомендую использовать .fullScreen вместо .overFullScreen. .fullScreen запускает viewWillAppear и viewDidAppear, .overFullScreen этого не делает
PiterPan

5
Время прошло, и .automaticстиль установился как стиль по умолчанию, который является (для большинства контроллеров представления) .pageSheetстилем. Однако некоторые контроллеры системного представления могут отображать его в другом стиле.
Якуб Трухларж

186

Я добавляю информацию, которая может быть полезна для кого-то. Если у вас есть какой-либо сценарий, чтобы вернуться к старому стилю, вам нужно установить для свойства kind значение Present Modally, а для свойства Presentation - Full Screen .

введите описание изображения здесь


2
Есть ли способ в Интерфейсном Разработчике установить isModalInPresentation?
Брэд Томас

Согласитесь с этим. Измените значение презентации для оскорбительных представлений самого высокого уровня.
Дэвид

Это тот, который работал для меня, потому что у меня есть performSegue. Спасибо, дорогой!
Бласанка

95

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

fix_storyboard_presentation_default_behavior


2
Есть ли способ сделать это программно для всех представлений вместо одного за другим через раскадровку?
1993 год

@ The1993 Ты нашел альтернативу этому?
Шобхит Пури

1
@ShobhitPuri Посмотрите на первое решение от Omreyh здесь stackoverflow.com/a/58255416/4323101
The1993

1
Вау, это был ответ на мои вопросы. Спасибо за совет! Для всех остальных, кто изучает это, это исправление странного поведения после повторного открытия приложения из фона. В моем приложении открытие из фона наложило бы мой экран-заставку (начальный контроллер представления) в качестве стиля представления карты, и тогда любые переходы с этого момента будут затухать вместо использования моего определенного стиля перехода. Было бы хорошо, если бы я закрыл приложение (дважды нажмите кнопку «Домой» и проведите пальцем вверх, а затем снова откройте), но любые дополнительные запуски вызовут это странное поведение. Еще раз спасибо!
Джастин

92

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

1- переопределить настоящее

Если у вас есть, BaseViewControllerвы можете переопределить present(_ viewControllerToPresent: animated flag: completion:)метод.

class BaseViewController: UIViewController {

  // ....

  override func present(_ viewControllerToPresent: UIViewController,
                        animated flag: Bool,
                        completion: (() -> Void)? = nil) {
    viewControllerToPresent.modalPresentationStyle = .fullScreen
    super.present(viewControllerToPresent, animated: flag, completion: completion)
  }

  // ....
}

Используя этот способ, вам не нужно вносить никаких изменений в любой presentвызов, так как мы просто переопределили presentметод.

2- Расширение:

extension UIViewController {
  func presentInFullScreen(_ viewController: UIViewController,
                           animated: Bool,
                           completion: (() -> Void)? = nil) {
    viewController.modalPresentationStyle = .fullScreen
    present(viewController, animated: animated, completion: completion)
  }
}

Применение:

presentInFullScreen(viewController, animated: true)

3- Для одного UIViewController

let viewController = UIViewController()
viewController.modalPresentationStyle = .fullScreen
present(viewController, animated: true, completion: nil)

4- из раскадровки

Выберите переход и установите презентацию на FullScreen.
введите описание изображения здесь

5- Swizzling

extension UIViewController {

  static func swizzlePresent() {

    let orginalSelector = #selector(present(_: animated: completion:))
    let swizzledSelector = #selector(swizzledPresent)

    guard let orginalMethod = class_getInstanceMethod(self, orginalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else{return}

    let didAddMethod = class_addMethod(self,
                                       orginalSelector,
                                       method_getImplementation(swizzledMethod),
                                       method_getTypeEncoding(swizzledMethod))

    if didAddMethod {
      class_replaceMethod(self,
                          swizzledSelector,
                          method_getImplementation(orginalMethod),
                          method_getTypeEncoding(orginalMethod))
    } else {
      method_exchangeImplementations(orginalMethod, swizzledMethod)
    }

  }

  @objc
  private func swizzledPresent(_ viewControllerToPresent: UIViewController,
                               animated flag: Bool,
                               completion: (() -> Void)? = nil) {
    if #available(iOS 13.0, *) {
      if viewControllerToPresent.modalPresentationStyle == .automatic {
        viewControllerToPresent.modalPresentationStyle = .fullScreen
      }
    }
    swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
   }
}

Использование:
В вашем AppDelegateвнутреннемapplication(_ application: didFinishLaunchingWithOptions) добавьте эту строку:

UIViewController.swizzlePresent()

Используя этот способ, вам не нужно вносить никаких изменений в любой текущий вызов, так как мы заменяем текущую реализацию метода во время выполнения.
Если вам нужно знать, что происходит, вы можете проверить эту ссылку: https://nshipster.com/swift-objc-runtime/


У меня много viewControllers в моем проекте, но у меня нет базового класса, я не хочу, чтобы у вас было какое-то решение, есть ли у вас какое-то решение для этого с минимальными изменениями в коде
гуру

Я использовал swizzling, но я добавил .pageSheet к условию .... if viewControllerToPresent.modalPresentationStyle == .pageSheet || viewControllerToPresent.modalPresentationStyle == .automatic {viewControllerToPresent.modalPresentationStyle = .fullScreen}
Сбой

строка состояния скрывается, когда я добавляю примененное решение № 1.
Ганеш Павар

4
Swizzling - это решение, которое какое-то время прекрасно работало. Однако при использовании некоторых внешних SDK, таких как FacebookLogin (на сегодняшний день - 5.8) и GoogleSignin, я заметил, что этот метод разрывает эти потоки: у нас на iPad белый экран. Вероятно, это связано с тем, что они используют свой собственный метод мерзлоты
Тао-Нхан Нгуен

39

Я использовал Swizzling для IOS 13

import Foundation
import UIKit

private func _swizzling(forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
    if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
       let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

extension UIViewController {

    static let preventPageSheetPresentation: Void = {
        if #available(iOS 13, *) {
            _swizzling(forClass: UIViewController.self,
                       originalSelector: #selector(present(_: animated: completion:)),
                       swizzledSelector: #selector(_swizzledPresent(_: animated: completion:)))
        }
    }()

    @available(iOS 13.0, *)
    @objc private func _swizzledPresent(_ viewControllerToPresent: UIViewController,
                                        animated flag: Bool,
                                        completion: (() -> Void)? = nil) {
        if viewControllerToPresent.modalPresentationStyle == .pageSheet
                   || viewControllerToPresent.modalPresentationStyle == .automatic {
            viewControllerToPresent.modalPresentationStyle = .fullScreen
        }
        _swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
    }
}

тогда положи это

UIViewController.preventPageSheetPresentation

где-то

например в AppDelegate

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {

    UIViewController.preventPageSheetPresentation
    // ...
    return true
}

3
Самое простое решение, если у вас есть несколько модалов.
Raisal Calicut

где-то означает, означает, в каком файле или где во всем проекте
Yatendra

где-то ты, я помещаю это в appdelegate static let, ленив, ты должен прочитать это, чтобы позволить этому быть вычисленным, ленивый var вычислен только один раз, я использую это, чтобы гарантировать, что swizzling вызвал однажды
Максим Ашуров

37

Для пользователей Objective-C

Просто используйте этот код

 [vc setModalPresentationStyle: UIModalPresentationFullScreen];

Или, если вы хотите добавить его в iOS 13.0, используйте

 if (@available(iOS 13.0, *)) {
     [vc setModalPresentationStyle: UIModalPresentationFullScreen];
 } else {
     // Fallback on earlier versions
 }

2
это работает и на более ранней версии ios, но ответ хороший
Catalin

4
UIModalPresentationFullScreen работает с iOS 3.2+. Поэтому нет необходимости добавлять, если еще условие.
flame3

2
По некоторым причинам в iOS 13.1.2 только в классах Obj-c это не работает, и modalPresentationStyle отображает только pageSheet. Это происходит для кого-то еще?
19

@ Sevy11 Я не обновился до iOS 13.1.2, но работаю нормально в 13.1
9to5ios

Привет @ Sevy11 Я обновил мою iOS 13.1.2 и она работает нормально, пожалуйста, проверьте на вашем конце
9to5ios

37

Один лайнер:

modalPresentationStyleдолжен быть установлен на навигационном контроллере, который представлен .


iOS 13 и ниже iOS версия fullScreen с overCurrentContextи navigationController

Протестированный код

let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
navigationController.modalPresentationStyle = .overCurrentContext
self.navigationController?.present(navigationController, animated: true, completion: nil)

modalPresentationStyle требуется установить в navigationController .


Пояснение: для представления viewController из navigationController не требуется устанавливать modalPresentationStyle в navigationController. Вместо этого он устанавливается в представляемом viewController. Однако, если вы представляете навигационный контроллер, тогда свойство 'modalPresentationStyle' должно быть установлено на навигационный контроллер, а не на встроенный viewController. Этот подход работает iOS 13.3, Xcode 11.3. Смотрите ответ Йогеша Бхарате.
Уомбл

@ Пратик Содха. Любая помощь в этом? stackoverflow.com/questions/61429600/…
Дэвид

@Womble Любая помощь на этом stackoverflow.com/questions/61429600/…
Дэвид

25

Как подсказка: если вы вызываете подарок, ViewControllerкоторый встроен в, NavigationControllerвы должны установить NavigationControllerдля.fullScreen а не для VC.

Вы можете сделать это как @davidbates или сделать это программно (например, @pascalbros).

Пример сценария:

введите описание изображения здесь

    //BaseNavigationController: UINavigationController {}
    let baseNavigationController = storyboard!.instantiateViewController(withIdentifier: "BaseNavigationController")
    var navigationController = UINavigationController(rootViewController: baseNavigationController)
    navigationController.modalPresentationStyle = .fullScreen
    navigationController.topViewController as? LoginViewController
    self.present(navigationViewController, animated: true, completion: nil)

22

Мне нужно было сделать оба:

  1. Установить стиль презентации на полный экран

    Полноэкранный

  2. Установить верхнюю панель как полупрозрачную панель навигации

Верхняя панель


Это очень странное предположение, что какой-то параметр в Simulated Metrics поможет вам изменить стиль презентации
Александр Кулабухов

14

Изменить modalPresentationStyleперед представлением

vc.modalPresentationStyle = UIModalPresentationFullScreen;

13

Вот простое решение без кодирования одной строки.

  • Выберите View Controller в раскадровке
  • Выберите атрибут инспектора
  • Установите презентацию «Автоматически» на «Полный экран», как показано на рисунке ниже.

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

введите описание изображения здесь


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

1
также убедитесь, что последующие сегменты "Показать", а не "Представить модально"
Энди Вайнштейн

Хорошо, так что это, кажется, работает для меня, когда я использую контроллер панели вкладок для управления другими представлениями. Тем не менее, вам нужно установить представление фактического «контроллера панели вкладок» на полный экран, так как это контролирует все другие представления.
Чарли

12

Если у вас есть UITabController с экранами со встроенными контроллерами навигации, вы должны установить презентацию UITabController на FullScreen, как показано на рисунке ниже.

введите описание изображения здесь


12

Вот решение для Objective-C

UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ViewController *vc = [storyBoard instantiateViewControllerWithIdentifier:@"ViewController"];

vc.modalPresentationStyle = UIModalPresentationFullScreen;

[self presentViewController:vc animated:YES completion:nil];


9

Вот моя версия исправления в ObjectiveC с использованием категорий. При таком подходе вы будете иметь поведение UIModalPresentationStyleFullScreen по умолчанию, пока не будет явно задано другое.

#import "UIViewController+Presentation.h"
#import "objc/runtime.h"

@implementation UIViewController (Presentation)

- (void)setModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle {
    [self setPrivateModalPresentationStyle:modalPresentationStyle];
}

-(UIModalPresentationStyle)modalPresentationStyle {
    UIModalPresentationStyle style = [self privateModalPresentationStyle];
    if (style == NSNotFound) {
        return UIModalPresentationFullScreen;
    }
    return style;
}

- (void)setPrivateModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle {
    NSNumber *styleNumber = [NSNumber numberWithInteger:modalPresentationStyle];
     objc_setAssociatedObject(self, @selector(privateModalPresentationStyle), styleNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIModalPresentationStyle)privateModalPresentationStyle {
    NSNumber *styleNumber = objc_getAssociatedObject(self, @selector(privateModalPresentationStyle));
    if (styleNumber == nil) {
        return NSNotFound;
    }
    return styleNumber.integerValue;
}

@end

у вас есть файл .h?
Педро Гоес

@ PedroGóes да, но он состоит только из объявления категории: `` `@interface UIViewController (Presentation) @end` ``
Александр Кулабухов

Используйте это, если вы не хотите иметь дело со всеми контроллерами в вашей раскадровке! Экономия времени, спасибо!
AnthoPak

7

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

введите описание изображения здесь

Для тех, кто активно использует раскадровку. Это мой совет: используйте Regex.

Следующий формат не подходит для полноэкранных страниц:

<segue destination="Bof-iQ-svK" kind="presentation" identifier="importSystem" modalPresentationStyle="fullScreen" id="bfy-FP-mlc"/>

Следующий формат подходит для полноэкранных страниц:

<segue destination="7DQ-Kj-yFD" kind="presentation" identifier="defaultLandingToSystemInfo" modalPresentationStyle="fullScreen" id="Mjn-t2-yxe"/>

Следующее регулярное выражение, совместимое с VS CODE, преобразует все страницы старого стиля в новые страницы стиля. Вам может понадобиться экранировать специальные символы, если вы используете другие движки регулярных выражений / текстовые редакторы.

Поиск регулярных выражений

<segue destination="(.*)"\s* kind="show" identifier="(.*)" id="(.*)"/>

Заменить регулярное выражение

<segue destination="$1" kind="presentation" identifier="$2" modalPresentationStyle="fullScreen" id="$3"/>

4

Первоначально значение по умолчанию fullscreenдля modalPresentationStyle, но в iOS 13 его изменения в UIModalPresentationStyle.automatic.

Если вы хотите , чтобы контроллер полного экрана , вы должны изменить modalPresentationStyleк fullScreen.

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


Это правильный ответ для iOS 13.3, Xcode 11.3, для ситуаций, когда вы хотите представить viewController ОТ навигационного контроллера. Также работает модальный стиль представления .overFullScreen. Однако, если вы представляете навигационный контроллер, то вам нужно установить 'modalPresentationStyle' на навигационный контроллер, а не на viewController. Приветствия.
Уомбл

3

Вы можете легко сделать это. Откройте раскадровку как исходный код и выполните поиск kind="presentation", во всех тегах seague с kind = presentation добавьте дополнительный атрибут.modalPresentationStyle="fullScreen"


2
let Obj = MtViewController()
Obj.modalPresentationStyle = .overFullScreen
self.present(Obj, animated: true, completion: nil)

// если вы хотите отключить смахивание, чтобы закрыть его, добавьте строку

Obj.isModalInPresentation = true

Проверьте Apple Document для получения дополнительной информации.


2

Я добился этого с помощью метода Swizzling (Swift 4.2):

Чтобы создать расширение UIViewController следующим образом

extension UIViewController {

    @objc private func swizzled_presentstyle(_ viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?) {

        if #available(iOS 13.0, *) {
            if viewControllerToPresent.modalPresentationStyle == .automatic || viewControllerToPresent.modalPresentationStyle == .pageSheet {
                viewControllerToPresent.modalPresentationStyle = .fullScreen
            }
        }

        self.swizzled_presentstyle(viewControllerToPresent, animated: animated, completion: completion)
    }

     static func setPresentationStyle_fullScreen() {

        let instance: UIViewController = UIViewController()
        let aClass: AnyClass! = object_getClass(instance)

        let originalSelector = #selector(UIViewController.present(_:animated:completion:))
        let swizzledSelector = #selector(UIViewController.swizzled_presentstyle(_:animated:completion:))

        let originalMethod = class_getInstanceMethod(aClass, originalSelector)
        let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector)
        if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
        method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}

и в AppDelegate, в приложении: didFinishLaunchingWithOptions: вызвать сверкающий код, вызвав:

UIViewController.setPresentationStyle_fullScreen()

1

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

yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen


3
Это ничего не добавляет ко всем другим существующим ответам.
rmaddy

1

Создайте категорию для UIViewController (скажем, UIViewController + PresentationStyle). Добавьте следующий код к нему.

 -(UIModalPresentationStyle)modalPresentationStyle{
     return UIModalPresentationStyleFullScreen;
}

это сломает UISearchController + вызовет сложную отладку сбоев
dklt

@dklt Это отличное наблюдение. Таким образом, установка свойства явно решит проблему. Нет простых решений, если используется UISearchController.
Говинд

0

альтернативный подход состоит в том, чтобы иметь свой собственный базовый компонент viewcontroller в вашем приложении и просто реализовать назначенные и требуемые инициализаторы с базовой настройкой, что-то вроде следующего:

class MyBaseViewController: UIViewController {

//MARK: Initialisers

/// Alternative initializer which allows you to set the modal presentation syle
/// - Parameter modalStyle: the presentation style to be used
init(with modalStyle:UIModalPresentationStyle) {
    super.init(nibName: nil, bundle: nil)
    self.setup(modalStyle: modalStyle)
}

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    // default modal presentation style as fullscreen
    self.setup(modalStyle: .fullScreen)
}

required init?(coder: NSCoder) {
    super.init(coder: coder)
    // default modal presentation style as fullscreen
    self.setup(modalStyle: .fullScreen)
}

//MARK: Private

/// Setup the view
///
/// - Parameter modalStyle: indicates which modal presentation style to be used
/// - Parameter modalPresentation: default true, it prevent modally presented view to be dismissible with the default swipe gesture
private func setup(modalStyle:UIModalPresentationStyle, modalPresentation:Bool = true){
    if #available(iOS 13, *) {
        self.modalPresentationStyle = modalStyle
        self.isModalInPresentation = modalPresentation
    }
}

НОТА . Если ваш контроллер представления содержится в навигационном контроллере, который фактически представлен модально, тогда навигационный контроллер должен подходить к проблеме таким же образом (то есть настраивать свой пользовательский компонент контроллера навигации таким же образом.

Протестировано на Xcode 11.1 на iOS 13.1 и iOS 12.4

Надеюсь, поможет


0

У меня была эта проблема с видео, не представляющим полноэкранный режим. Добавил эту строчку, которая спасла день :-)

videoController.modalPresentationStyle = UIModalPresentationFullScreen;

2
Это ничего не добавляет ко всем другим существующим ответам.
rmaddy

0

Самое простое решение, которое сработало для меня.

viewController.modalPresentationStyle = .fullScreen

3
Это ничего не добавляет ко всем другим существующим ответам.
rmaddy

0

Если вы используете UINavigationController и встраиваете ViewController в качестве корневого контроллера представления, то вы также столкнетесь с такой же проблемой. Используйте следующий код для преодоления.

let vc = UIViewController()
let navController = UINavigationController(rootViewController: vc)
navController.modalPresentationStyle = .fullScreen

0
class MyViewController: UIViewController {

    convenience init() {
        self.init(nibName:nil, bundle:nil)
        self.modalPresentationStyle = .fullScreen
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

Вместо того чтобы вызывать self.modalPresentationStyle = .fullScreenкаждый контроллер представления, вы можете создать подкласс UIViewController и использовать его MyViewControllerвезде.


-1

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

# 1 Создано расширение UIView

# 2 Создал метод ()

//#1
extension UIViewController {

//#2
func presentLocal(_ viewControllerToPresent: UIViewController, animated flag: 
Bool, completion: (() -> Void)? = nil) {

//Reusing below 2 lines :-)
viewControllerToPresent.modalPresentationStyle = .overCurrentContext
self.present(viewControllerToPresent, animated: flag, completion: completion)

  }
}

Вызывая как показано ниже

let vc = MyViewController()
let nc = UINavigationController(rootViewController: vc)
sourceView.presentLocal(nc, animated: true, completion: nil)

ИЛИ

let vc = MyViewController()
sourceView.presentLocal(vc, animated: true, completion: nil)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.