web-dev-qa-db-fra.com

Présentation du modal en plein écran sur iOS 13

Dans iOS 13, il existe un nouveau comportement pour le contrôleur de vue modale lors de sa présentation.

Maintenant, ce n'est pas plein écran par défaut et lorsque j'essaie de glisser vers le bas, l'application ferme simplement le contrôleur de vue automatiquement.

Comment puis-je empêcher ce comportement et revenir à l'ancien modal vc plein écran?

modal behaviour

Merci

426
pascalbros

Avec iOS 13, comme indiqué dans Platforms State of the Union lors de la WWDC 2019, Apple a introduit une nouvelle présentation de carte par défaut. Afin de forcer le plein écran, vous avez le spécifier explicitement avec:

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

J'ajoute une information qui pourrait être utile à quelqu'un. Si vous avez une séquence de storyboard, pour revenir à l'ancien style, vous devez définir la propriété kind sur Présentez Modally et la propriété Presentation dans Plein écran .

enter image description here

159
Alessandro

J'ai eu ce problème sur la vue initiale juste après l'écran de lancement. Le correctif pour moi puisque je n'avais pas de séquence ou de logique définie était de basculer la présentation de l'automatique au plein écran comme indiqué ici:

fix_storyboard_presentation_default_behavior

83
davidbates

Il y a plusieurs façons de le faire, et je pense que chacun pourrait convenir à un projet mais pas à un autre, alors j'ai pensé que je les garderais ici, peut-être que quelqu'un d'autre ira dans un cas différent.

1- Priorité présente

Si vous avez un BaseViewController, vous pouvez remplacer la méthode 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)
  }

  // ....
}

De cette façon, vous n'avez pas besoin de faire de changement sur un appel present, car nous venons de remplacer la méthode present.

2- Une extension:

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

Usage:

presentInFullScreen(viewController, animated: true)

3- Pour un UIViewController

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

4- Du Storyboard

Sélectionnez une séquence et définissez la présentation sur FullScreen.
enter image description here

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

Usage:
Dans votre AppDelegate à l'intérieur application(_ application: didFinishLaunchingWithOptions) ajoutez cette ligne:

UIViewController.swizzlePresent()

De cette façon, vous n'avez pas besoin de faire de changement sur un appel en cours, car nous remplaçons l'implémentation de la méthode actuelle dans le runtime.
Si vous avez besoin de savoir ce qui se passe, vous pouvez vérifier ce lien: https://nshipster.com/Swift-objc-runtime/

76

Pour les utilisateurs d'Objective-C

Utilisez simplement ce code

 [vc setModalPresentationStyle: UIModalPresentationFullScreen];

Ou si vous souhaitez l'ajouter en particulier dans iOS 13.0, utilisez

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

J'ai utilisé swizzling pour 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)
    }
}

puis mettez ça

UIViewController.preventPageSheetPresentation

quelque part

par exemple dans AppDelegate

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

    UIViewController.preventPageSheetPresentation
    // ...
    return true
}
31
Maxime Ashurov

Bon mot:

modalPresentationStyle doit être défini sur le navigationController qui est présenté.


iOS 13 et versions ultérieures iOS plein écran avec overCurrentContext et navigationController

Code testé

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

modalPresentationStyle nécessite de définir à navigationController.

27
Pratik Sodha

À titre indicatif: si vous appelez present à un ViewController qui est incorporé dans un NavigationController, vous devez définir le NavigationController sur .fullScreen et non le VC.

Vous pouvez le faire comme @davidbates ou vous le faites par programme (comme @pascalbros).

Un exemple de scénario:

enter image description here

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

Je devais faire les deux:

  1. Définir le style de présentation en plein écran

    Full screen

  2. Définir la barre supérieure comme barre de navigation translucide

Top bar

12
vedrano

Voici la solution pour Objective-C

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

vc.modalPresentationStyle = UIModalPresentationFullScreen;

[self presentViewController:vc animated:YES completion:nil];
11
Prashanth Thota

Si vous avez un UITabController avec des écrans avec des contrôleurs de navigation intégrés, vous devez définir la présentation UITabController sur FullScreen comme indiqué sur la photo ci-dessous

enter image description here

11
Di Nerd

Voici une solution simple sans coder une seule ligne.

  • Sélectionnez Afficher le contrôleur dans le Storyboard
  • Sélectionner un inspecteur d'attributs
  • Réglez la présentation "Automatique" sur "Plein écran" comme sur l'image ci-dessous

Cette modification rend le comportement de l'application iPad comme prévu, sinon le nouvel écran s'affiche au centre de l'écran sous forme de popup.

enter image description here

11
Mahesh Cheliya

Voici ma version du correctif dans Objective C en utilisant les catégories. Avec cette approche, vous aurez le comportement ModalPresentationStyle FullScreen par défaut jusqu'à ce qu'un autre soit défini explicitement.

#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
8

Toutes les autres réponses sont suffisantes, mais pour un grand projet comme le nôtre et où les navigations sont effectuées à la fois dans le code et le storyboard, c'est une tâche assez intimidante.

enter image description here

Pour ceux qui utilisent activement Storyboard. Voici mon conseil: utilisez Regex.

Le format suivant n'est pas adapté aux pages plein écran:

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

Le format suivant convient aux pages plein écran:

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

Le regex suivant compatible avec VS CODE convertira toutes les pages de l'ancien style en nouvelles pages de style. Vous devrez peut-être échapper des caractères spéciaux si vous utilisez d'autres moteurs/éditeurs de texte regex.

Rechercher Regex

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

Remplacer Regex

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

Initialement, la valeur par défaut est fullscreen pour modalPresentationStyle, mais dans iOS 13 ses changements dans le UIModalPresentationStyle.automatic.

Si vous voulez créer le contrôleur de vue plein écran, vous devez changer le modalPresentationStyle en fullScreen.

Reportez-vous à UIModalPresentationStyledocumentation Apple pour plus de détails et reportez-vous à directives de l'interface utilisateur Apple pour savoir où utiliser quelle modalité.

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

// si vous souhaitez désactiver le glissement pour le supprimer, ajoutez une ligne

Obj.isModalInPresentation = true

Vérifiez Document Apple pour plus d'informations.

2
Mohit Tomar

Vous pouvez facilement le faire Ouvrez votre storyboard en tant que code source et recherchez kind="presentation", dans toutes les balises seague avec kind = presentation ajoutez un attribut supplémentaire modalPresentationStyle="fullScreen"

2
amar

Créez une catégorie pour UIViewController (par exemple UIViewController + PresentationStyle). Ajoutez-y le code suivant.

 -(UIModalPresentationStyle)modalPresentationStyle{
     return UIModalPresentationStyleFullScreen;
}
1
Govind

Cela a fonctionné pour moi:

yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen

1
Dian

J'ai eu ce problème avec une vidéo ne présentant plus le plein écran. Ajout de cette ligne, qui a sauvé la journée :-)

videoController.modalPresentationStyle = UIModalPresentationFullScreen;
0
user1737746
class MyViewController: UIViewController {

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

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

Plutôt que d'appeler self.modalPresentationStyle = .fullScreen pour chaque contrôleur de vue, vous pouvez sous-classer UIViewController et simplement utiliser MyViewController partout.

0
Peter Tao

changer modalPresentationStyle avant de présenter

vc.modalPresentationStyle = UIModalPresentationFullScreen;

0
MAMN84

une autre approche consiste à avoir votre propre composant de base viewcontroller dans votre application, et à simplement implémenter les initialiseurs désignés et requis avec une configuration de base, quelque chose comme ceci:

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
    }
}

NOTE : Si votre contrôleur de vue est contenu dans un contrôleur de navigation qui est en fait présenté de façon modale, alors le contrôleur de navigation doit aborder le problème de la même manière (ce qui signifie, avoir votre composant de contrôleur de navigation personnalisé personnalisé de la même manière

Testé sur Xcode 11.1 sur iOS 13.1 et iOS 12.4

J'espère que ça aide

0
Luca Iaco

Dernière version pour iOS 13 et Swift 5.x

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

vc.modalPresentationStyle = .fullScreen

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

Si vous utilisez un UINavigationController et intégrez un ViewController en tant que contrôleur de vue racine, vous rencontrerez également le même problème. Utilisez le code suivant pour surmonter.

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

La solution la plus simple qui a fonctionné pour moi.

viewController.modalPresentationStyle = .fullScreen
0
Fahad Azeem