Cambia modalPresentationStyle su iOS13 su tutte le istanze di UIViewController contemporaneamente usando il metodo swizzling


11

[Domande e risposte] È possibile modificare il UIViewController.modalPresentationStylevalore a livello globale su iOS 13 in modo che si comporti come su iOS 12 (o precedente)?


Perché?

In iOS SDK 13 il valore predefinito della UIViewController.modalPresentationStyleproprietà è stato modificato rispetto UIModalPresentationFullScreena UIModalPresentationAutomaticche è, per quanto ne so, ha deliberato di UIModalPresentationPageSheetsu dispositivi iOS o almeno su iPhone.

Dal momento che il progetto su cui lavoro da diversi anni è diventato piuttosto grande, ci sono decine di posti in cui viene presentato un controller di visualizzazione. Il nuovo stile di presentazione non corrisponde sempre al design delle nostre app e talvolta provoca un'interruzione dell'interfaccia utente. Per questo motivo, abbiamo deciso di UIViewController.modalPresentationStyletornare alle UIModalPresentationFullScreenversioni SDK precedenti a iOS13.

Ma aggiungere viewController.modalPresentationStyle = UIModalPresentationFullScreenprima di chiamare presentViewController:animated:completion:in ogni singolo posto in cui viene presentato un controller sembrava eccessivo. Inoltre, in quel momento avevamo questioni più serie da affrontare, motivo per cui, per il momento o almeno fino a quando non aggiorniamo i nostri progetti e risolviamo tutti i problemi dell'interfaccia utente, abbiamo deciso di adottare un approccio che sfrutta il metodo.

La soluzione di lavoro è presentata nella mia risposta, ma gradirei qualsiasi feedback che mi dica quali potrebbero essere gli svantaggi o le conseguenze di tale approccio.

Risposte:


12

Ecco come l'abbiamo ottenuto utilizzando il metodo swizzling:


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

veloce

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

e in AppDelegate, in application:didFinishLaunchingWithOptions:invoke the swizzling chiamando (solo versione rapida):

UIViewController.swizzlePresentationStyle()

Assicurati che venga chiamato solo una volta (usa dispatch_onceo qualche equivalente).


Maggiori informazioni sul metodo che sfrigola qui:


1
Un problema potrebbe essere in esecuzione su un iPad in cui si desidera effettivamente un foglio di pagina e non a schermo intero. Potresti voler aggiornare il tuo controllo per passare automaticamente allo schermo intero e farlo solo quando il controller della vista di presentazione ha tratti di larghezza compatti.
rmaddy,

Questa soluzione è buona? E se qualcuno volesse davvero presentare un ViewController come .pageSheet?
ibrahimyilmaz,

1
@ibrahimyilmaz quindi impostare viewController.modalPresentationStyleper .pageSheete call self.swizzled_present(:,:,:). Forse non è molto carino, ma l'intero punto di questo post era basato sul presupposto che hai già un progetto esistente con molte chiamate per la presentazione modale e vuoi ripristinare il comportamento pre-iOS13 senza aggiornare ogni riga di codice.
Bevoy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.