Modifiez modalPresentationStyle sur iOS13 sur toutes les instances UIViewController à la fois en utilisant la méthode swizzling

11

[Q&R] Est-il possible de changer la UIViewController.modalPresentationStylevaleur globalement sur iOS 13 afin qu'il se comporte comme il l'était sur iOS 12 (ou antérieur)?


Pourquoi?

Dans iOS SDK 13 la valeur par défaut de la UIViewController.modalPresentationStylepropriété a été modifié par rapport UIModalPresentationFullScreenà ce UIModalPresentationAutomaticqui est, pour autant que je sache, résolu à UIModalPresentationPageSheetsur les appareils iOS ou tout au moins sur les iPhones.

Depuis que le projet sur lequel je travaille depuis plusieurs années est devenu assez grand, il y a des dizaines d'endroits où un contrôleur de vue est présenté. Le nouveau style de présentation ne correspond pas toujours à la conception de nos applications et parfois, l'interface utilisateur se désagrège. C'est pourquoi, nous avons décidé de UIViewController.modalPresentationStylerevenir à UIModalPresentationFullScreenla version actuelle du SDK iOS13.

Mais ajouter viewController.modalPresentationStyle = UIModalPresentationFullScreenavant d'appeler presentViewController:animated:completion:dans chaque endroit où un contrôleur est présenté semblait être une exagération. De plus, nous avions des problèmes plus sérieux à régler à ce moment-là, c'est pourquoi, pour le moment ou du moins jusqu'à ce que nous mettions à jour nos conceptions et résolvions tous les problèmes d'interface utilisateur, nous avons décidé de suivre une approche accélérée.

La solution de travail est présentée dans ma réponse, mais j'apprécierais tout commentaire me disant quels inconvénients ou conséquences d'une telle approche pourraient être.

bevoy
la source

Réponses:

12

Voici comment nous y sommes parvenus en utilisant la méthode swizzling:


Objectif c

UIViewController + iOS13Fixes.h

#import <Foundation/Foundation.h>

@interface UIViewController (iOS13Fixes)
@end

UIViewController + iOS13Fixes.m

#import <objc/runtime.h>

@implementation UIViewController (iOS13Fixes)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        SEL originalSelector = @selector(presentViewController:animated:completion:);
        SEL swizzledSelector = @selector(swizzled_presentViewController:animated:completion:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL methodExists = !class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

        if (methodExists) {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        } else {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }
    });
}

- (void)swizzled_presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated completion:(void (^)())completion {

    if (@available(iOS 13.0, *)) {
        if (viewController.modalPresentationStyle == UIModalPresentationAutomatic || viewController.modalPresentationStyle == UIModalPresentationPageSheet) {
            viewController.modalPresentationStyle = UIModalPresentationFullScreen;
        }
    }

    [self swizzled_presentViewController:viewController animated:animated completion:completion];
}

@end

Rapide

UIViewController + iOS13Fixes.swift

import UIKit

@objc public extension UIViewController {

    private func swizzled_present(_ viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?) {

        if #available(iOS 13.0, *) {
            if viewControllerToPresent.modalPresentationStyle == .automatic || viewControllerToPresent.modalPresentationStyle == .pageSheet {
                viewControllerToPresent.modalPresentationStyle = .fullScreen
            }
        }

        self.swizzled_present(viewControllerToPresent, animated: animated, completion: completion)
    }

    @nonobjc private static let _swizzlePresentationStyle: Void = {
        let instance: UIViewController = UIViewController()
        let aClass: AnyClass! = object_getClass(instance)

        let originalSelector = #selector(UIViewController.present(_:animated:completion:))
        let swizzledSelector = #selector(UIViewController.swizzled_present(_:animated:completion:))

        let originalMethod = class_getInstanceMethod(aClass, originalSelector)
        let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector)

        if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
            if !class_addMethod(aClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) {
                method_exchangeImplementations(originalMethod, swizzledMethod)
            } else {
                class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
            }
        }
    }()

    @objc static func swizzlePresentationStyle() {
        _ = self._swizzlePresentationStyle
    }
}

et dans AppDelegate, en application:didFinishLaunchingWithOptions:invoquant le swizzling en appelant (version rapide uniquement):

UIViewController.swizzlePresentationStyle()

Assurez-vous qu'il n'est appelé qu'une seule fois (utilisez dispatch_onceou un équivalent).


Plus d'informations sur la méthode swizzling ici:

bevoy
la source
1
Un problème serait de fonctionner sur un iPad où vous voulez réellement une feuille de page et non en plein écran. Vous souhaiterez peut-être mettre à jour votre chèque pour ne changer automatique en plein écran et ne le faire que lorsque le contrôleur de vue de présentation a des traits de largeur compacts.
rmaddy
Cette solution est-elle bonne? Que faire si quelqu'un veut vraiment présenter un ViewController en tant que .pageSheet?
ibrahimyilmaz
1
@ibrahimyilmaz vous mis viewController.modalPresentationStyleà .pageSheetet appel self.swizzled_present(:,:,:). Ce n'est peut-être pas très joli, mais tout l'intérêt de cet article était basé sur l'hypothèse que vous avez déjà un projet existant avec beaucoup d'appels pour une présentation modale et que vous souhaitez restaurer le comportement pré-iOS13 sans mettre à jour chaque ligne de code.
bevoy