Представление модального в 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.
У меня была эта проблема на начальном экране сразу после экрана запуска. Для меня, поскольку у меня не было определенной последовательности или логики, было переключение презентации с автоматического на полноэкранный режим, как показано здесь:
Есть несколько способов сделать это, и я думаю, что каждый из них может подойти для одного проекта, но не подходит для другого, поэтому я подумал, что оставлю их здесь, возможно, кто-то другой перейдет к другому делу.
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)
Мне нужно было сделать и то, и другое:
Установить стиль презентации как полноэкранный
Установить верхнюю панель как полупрозрачную панель навигации
Быстрое решение. Выше уже есть действительно отличные ответы. Я также добавляю свой быстрый ввод двух точек, который представлен на скриншоте.
Если вы не используете
Navigation Controller
затем изRight Menu Inspector
установить презентацию наFull Screen
Если вы используете
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];
}
- обмен методом