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?
Merci
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)
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.
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
.
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)
let viewController = UIViewController()
viewController.modalPresentationStyle = .fullScreen
present(viewController, animated: true, completion: nil)
Sélectionnez une séquence et définissez la présentation sur FullScreen
.
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/
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
}
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
}
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
etnavigationController
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.
À 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:
//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)
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];
Voici une solution simple sans coder une seule ligne.
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.
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
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.
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"/>
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 à UIModalPresentationStyle
documentation Apple pour plus de détails et reportez-vous à directives de l'interface utilisateur Apple pour savoir où utiliser quelle modalité.
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.
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"
Créez une catégorie pour UIViewController (par exemple UIViewController + PresentationStyle). Ajoutez-y le code suivant.
-(UIModalPresentationStyle)modalPresentationStyle{
return UIModalPresentationStyleFullScreen;
}
Cela a fonctionné pour moi:
yourViewController.modalPresentationStyle = UIModalPresentationStyle.fullScreen
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;
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.
changer modalPresentationStyle avant de présenter
vc.modalPresentationStyle = UIModalPresentationFullScreen;
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
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)
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
La solution la plus simple qui a fonctionné pour moi.
viewController.modalPresentationStyle = .fullScreen