Представлення модальних даних на повноекранному екрані iOS 13


464

У iOS 13 є нова поведінка для контролера модального перегляду при його представленні.

Тепер він не є повноекранним за замовчуванням, і коли я намагаюся ковзати вниз, програма просто відхиляє контролер перегляду.

Як я можу запобігти такій поведінці та повернутися до старого полноекранного модального відеокамери?

модальна поведінка

Дякую

Відповіді:


622

Завдяки iOS 13, як зазначено у Стані платформ Союзу під час WWDC 2019, Apple представила нову презентацію картки за замовчуванням. Щоб примусити повноекранний екран, його потрібно чітко вказати:

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

81
Я б сказав, що за замовчуванням це не буде довго. fullScreenОпція повинна бути по замовчуванням для запобігання розриву змін користувальницького інтерфейсу.
Jakub Truhlář

17
Я на це не буду розраховувати. У минулому Apple часто змінювала параметри за замовчуванням, лише активізуючись, коли ви пов'язуєтесь із поточним SDK. Ми сподіваємось отримати стару поведінку під час посилання на попередні версії.
DrMickeyLauer

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

5
Я рекомендую використовувати .fullScreen замість .overFullScreen. .fullScreen пожежі viewWillAppear and viewDidAppear, .overFullScreen цього не робить
PiterPan

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

186

Я додаю інформацію, яка може бути корисною для когось. Якщо у вас є якась розшифровка розкадрівки, щоб повернутися до старого стилю, вам потрібно встановити властивість виду на Present Modally, а властивість Presentation - на весь екран .

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


2
Чи існує спосіб у Interface Builder встановити isModalInPresentation?
Бред Томас

Погодьтеся з цим. Змініть значення Презентації для образотворчих представлень вищого рівня.
Девід

Це той, хто працював для мене, бо маю performSegue. Дякую, любий!
Бласанка

95

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

fix_storyboard_presentation_default_behavior


1
Чи є якийсь спосіб зробити це програмно для всіх переглядів, а не один за іншим через раскадровку?
The1993,

@ The1993 Ви знайшли альтернативу цьому?
Shobhit Puri

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- З Розгортки

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

5- шипіння

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, але не мають базового класу, я не хочу шикати, чи є у вас рішення для цього з мінімальними змінами коду
гуру

Я використав шиплячий, але я додав .pageSheet до умови .... якщо viewControllerToPresent.modalPresentationStyle == .pageSheet || viewControllerToPresent.modalPresentationStyle == .automatic {viewControllerToPresent.modalPresentationStyle = .fullScreen}
Збій

рядок стану ховається, коли я додаю застосоване рішення № 1.
Ганеш Павар

4
Швидкість - це рішення, яке працювало чудово протягом певного часу. Однак, використовуючи деякі зовнішні sdks, такі як FacebookLogin (5,8 станом на сьогодні) та GoogleSignin, я помітив, що цей метод порушує ці потоки: ми отримуємо білий екран на iPad. Можливо, це пов'язано з тим, що вони використовують свій власний метод шипування
Дао-Нхан Нгуен

39

Я використовував свистлінг для 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

десь означає, означає, в якому файлі або де в цілому проекті
Ятендра

десь ви, я поклав це в appdelegate static letлінивий, ви повинні прочитати його, щоб дозволити його обчислити, лінивий var обчислили лише один раз, і я використовую його, щоб гарантувати, що шиплячі дзвонили один раз
Максим

37

Для користувачів Objective-C

Просто використовуйте цей код

 [vc setModalPresentationStyle: UIModalPresentationFullScreen];

Або якщо ви хочете додати його конкретно в iOS 13.0, тоді використовуйте

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

2
це працює і на більш ранній версії ios, але відповідь хороша
Каталін

4
UIModalPresentationFullScreen працює з iOS 3.2+. Тому не потрібно додавати, якщо інша умова.
полум’я3

2
Чомусь в iOS 13.1.2 тільки в класах Obj-c це не працює, а modalPresentationStyle відображає лише сторінку аркуша. Це трапляється для когось іншого?
Sevy11

@ Sevy11 Я не оновлювався до iOS 13.1.2, але працюю чудово в 13.1
9to5ios

Привіт @ Sevy11 У мене є оновлення iOS 13.1.2 та його нормальної роботи, будь ласка, перевірте в кінці
9to5ios

36

Один вкладиш:

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 потрібно встановити в Навігаційний Контролер .


Пояснення: для подання представлення viewController з навігаційногоController не потрібно встановлювати modalPresentationStyle на navigationController. Натомість він встановлюється у представленому представленні viewController. Однак, якщо ви представляєте навігацію ControlController, то властивість 'modalPresentationStyle' слід встановити на navigationController, а не на вбудований viewController. Цей підхід працює на iOS 13.3, Xcode 11.3. Дивіться відповідь Йогеша Бхарата.
Womble

@Pratik Sodha. Будь-яка допомога з цього приводу? stackoverflow.com/questions/61429600/…
david

@Womble Будь-яка допомога в цьому stackoverflow.com/questions/61429600 / ...
Девід

25

В якості підказки: Якщо ви телефонуєте даний до ViewControllerякої впроваджені в NavigationControllerви повинні встановити NavigationControllerдля .fullScreenі не ВК.

Ви можете зробити це на кшталт @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)

20

Мені потрібно було зробити і те, і інше:

  1. Встановити стиль презентації як повний екран

    Повноекранний

  2. Встановити верхню панель як прозору панель навігації

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


Дуже дивне припущення, що якийсь варіант в Simulated Metrics допомагає змінити стиль викладу
Олександр Кулабухов,

14

Змініть modalPresentationStyleперед подачею

vc.modalPresentationStyle = UIModalPresentationFullScreen;

13

Ось просте рішення без кодування жодного рядка.

  • Виберіть Перегляд контролера на аркуші розгортки
  • Виберіть атрибут Inspector
  • Встановіть презентацію "Автоматично" на "Повний екран", як показано нижче зображення

Ця зміна робить поведінку додатків iPad так, як очікувалося, інакше новий екран відобразиться в центрі екрана як спливаюче вікно.

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


Я думаю, що тут важливо те, що ви зробили це на NavigationController. Це так, як показано на малюнку, але текст не говорить про це так.
Енді Вайнштейн

1
також переконайтесь, що наступні сеги - "Шоу", а не "Модний час"
Енді Вайнштейн

Гаразд, тому, здається, цей працює для мене, коли я використовую контролер панелі вкладок для управління іншими видами. Однак вам потрібно встановити презентацію фактичного "Контролера панелі вкладок" на весь екран, оскільки це контролює всі інші види.
Чарлі

12

Якщо у вас є UITabController зі екранами із вбудованими контролерами навігації, вам слід встановити презентацію UITabController на повний екран, як показано на малюнку нижче

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


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];


8

Ось моя версія виправлення в 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 (Презентація) @end `` `
Олександр Кулабухов

7

Всі інші відповіді є достатніми, але для такого масштабного проекту, як наш, і де здійснюються навігації як у коді, так і в дошці розповідей, це досить непросте завдання.

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

Для тих, хто активно використовує 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"/>

4

Спочатку значення за замовчуванням призначене fullscreenдля modalPresentationStyle, але в iOS 13 його зміни наUIModalPresentationStyle.automatic .

Якщо ви хочете , щоб контролер повного екрану , ви повинні змінити modalPresentationStyleдоfullScreen .

Докладніші відомості див. У UIModalPresentationStyle документації щодо яблук та перегляньте вказівки щодо інтерфейсу яблучного людського інтерфейсу, де слід застосовувати модальність.


Це правильна відповідь станом на iOS 13.3, Xcode 11.3 для ситуацій, коли ви хочете представити viewController ВІД навігаційного контролера. Модальна Репрезентація Стилю .overFullScreen також працює. Однак, якщо ви представляєте Навігаційний Контролер, Вам потрібно встановити "modalPresentationStyle" на Навігаційному Контролері, а не на ВиглядКонтролера. Ура.
Womble

3

Ви можете легко зробити це. Відкрийте свою kind="presentation"рекламну дошку як вихідний код і шукайте , у всьому тезі seague з kind = презентацією додайте додатковий атрибут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

Альтернативний підхід - мати у своєму додатку власний компонент базового контролера перегляду та просто реалізувати призначені та необхідні ініціалізатори з базовою установкою, як-от наступне:

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.