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

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

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

Спасибо

34 ответа

Решение

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

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

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

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

https://i.s tack.imgur.com/lnq2i.png

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

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

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

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

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

 [vc setModalPresentationStyle: UIModalPresentationFullScreen];

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

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

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

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

То же самое и с UITabViewController

Пример сценария для NavigationController:

https://s tackru.com/images/0c8ccc28f6a6c3606d03ba61ee4c90f090475b13.png

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

Один лайнер:

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


iOS 13 и ниже версии iOS полноэкранный с overCurrentContext а также navigationController

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

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

modalPresentationStyle необходимо установить в navigationController.

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

`let vc = self.storyboard?.instantiateViewController(withIdentifier: "cameraview1") as! CameraViewController

    vc.modalPresentationStyle = .fullScreen
    
self.present(vc, animated: true, completion: nil)`

Я использовал 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
}

Последняя версия для iOS 13 и Swift 5.x

let vc = ViewController(nibName: "ViewController", bundle: nil)

vc.modalPresentationStyle = .fullScreen

self.present(vc, animated: true, completion: nil)

Мне нужно было сделать и то, и другое:

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

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

Быстрое решение. Выше уже есть действительно отличные ответы. Я также добавляю свой быстрый ввод двух точек, который представлен на скриншоте.

  1. Если вы не используете Navigation Controller затем из Right Menu Inspector установить презентацию на Full Screen

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

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

vc.modalPresentationStyle = UIModalPresentationFullScreen;

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

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

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

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

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

vc.modalPresentationStyle = UIModalPresentationFullScreen;

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

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

Вот моя версия исправления в 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

Параметр navigationController.modalPresentationStyle к .fullScreen здесь повторяется более тысячи раз, но позвольте мне представить вам еще один блокировщик, который UIViewController / UINavigationController не показывался в fullscreen хотя все свойства были установлены правильно.

В моем случае виновник был скрыт в этой строке

      navigationController?.presentationController?.delegate = self

Видимо установка UIAdaptivePresentationControllerDelegate меняет стиль на pageSheet.

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

Для тех, кто активно пользуется Storyboard. Это мой совет: используйте 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, преобразует все страницы старого стиля в страницы нового стиля. Вам может потребоваться экранировать специальные символы, если вы используете другие движки регулярных выражений / текстовые редакторы.

Искать Regex

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

Заменить Regex

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

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

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

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

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

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

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

Obj.isModalInPresentation = true

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

Я добился этого с помощью метода 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 в application:didFinishLaunchingWithOptions: вызовите swizzling-код, вызвав:

UIViewController.setPresentationStyle_fullScreen()

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

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

 -(UIModalPresentationStyle)modalPresentationStyle{
     return UIModalPresentationStyleFullScreen;
}

override modalPresentationStyle исправит стиль для UIViewControllers , созданных с кодером или без него.

Преимущества:

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

Недостаток:

  • Вы не можете изменить его извне, например, конструктор интерфейса или конфигурацию из кода.

Решение:

      override var modalPresentationStyle: UIModalPresentationStyle {
    get { .fullScreen }
    set { }
}

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

yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen

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

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

Альтернативный подход состоит в том, чтобы иметь собственный компонент базового контроллера представления в вашем приложении и просто реализовать назначенные и необходимые инициализаторы с базовой настройкой, примерно так:

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

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

В Objective-C я нашел два способа.

Я заметил, что два enum modalPresentationStyles все меньше нуля

UIModalPresentationNone API_AVAILABLE(ios(7.0)) = -1,

UIModalPresentationAutomatic API_AVAILABLE(ios(13.0)) = -2,

  • переопределить ваш базовый метод ViewController (я предлагаю)
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
    if (viewControllerToPresent.modalPresentationStyle < 0){
        viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen;
    }
    [super presentViewController:viewControllerToPresent animated:flag completion:completion];
}
  • обмен методом
Другие вопросы по тегам