Ubah modalPresentationStyle di iOS13 di semua instance UIViewController sekaligus menggunakan metode swizzling

11

[T&J] Apakah mungkin untuk mengubah UIViewController.modalPresentationStylenilai secara global di iOS 13 sehingga berperilaku seperti dulu di iOS 12 (atau sebelumnya)?


Mengapa?

Di iOS 13 SDK nilai default dari UIViewController.modalPresentationStyleproperti telah diubah dari UIModalPresentationFullScreenke UIModalPresentationAutomaticyang, sejauh yang saya tahu, memutuskan untuk UIModalPresentationPageSheetpada perangkat iOS atau setidaknya pada iPhone.

Karena proyek saya telah bekerja selama beberapa tahun telah menjadi sangat besar, ada puluhan tempat di mana view controller disajikan. Gaya presentasi baru tidak selalu cocok dengan desain aplikasi kami dan kadang-kadang menyebabkan UI berantakan. Itulah sebabnya, kami memutuskan untuk UIViewController.modalPresentationStylekembali ke UIModalPresentationFullScreenversi sebelumnya iOS13 SDK.

Tetapi menambahkan viewController.modalPresentationStyle = UIModalPresentationFullScreensebelum memanggil presentViewController:animated:completion:di setiap tempat di mana controller disajikan sepertinya berlebihan. Selain itu, kami memiliki masalah yang lebih serius untuk ditangani pada saat itu, itulah sebabnya, untuk saat ini atau setidaknya sampai kami memperbarui desain kami dan memperbaiki semua masalah UI, kami memutuskan untuk menggunakan metode pendekatan swizzling.

Solusi yang berfungsi disajikan dalam jawaban saya, tetapi saya akan menghargai setiap umpan balik yang memberi tahu saya apa kerugian atau konsekuensi dari pendekatan semacam itu.

bevoy
sumber

Jawaban:

12

Inilah cara kami mencapainya dengan menggunakan metode swizzling:


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

Cepat

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

dan in AppDelegate, application:didFinishLaunchingWithOptions:aktifkan swizzling dengan memanggil (hanya versi swift):

UIViewController.swizzlePresentationStyle()

Pastikan hanya dipanggil sekali (gunakan dispatch_onceatau setara).


Lebih lanjut tentang metode swizzling di sini:

bevoy
sumber
1
Satu masalah akan berjalan di iPad di mana Anda sebenarnya menginginkan lembar halaman dan bukan layar penuh. Anda mungkin ingin memperbarui pemeriksaan Anda untuk hanya mengubah otomatis ke layar penuh dan hanya melakukannya ketika pengontrol tampilan yang disajikan memiliki sifat lebar yang kompak.
rmaddy
Apakah solusi ini baik? Bagaimana jika seseorang benar-benar ingin menghadirkan ViewController sebagai .pageSheet?
ibrahimyilmaz
1
@ibrahimyilmaz maka Anda mengatur viewController.modalPresentationStyleke .pageSheetdan memanggil self.swizzled_present(:,:,:). Mungkin tidak terlalu cantik, tetapi inti dari postingan ini didasarkan pada asumsi bahwa Anda sudah memiliki proyek yang luas dengan banyak panggilan untuk presentasi modal dan Anda ingin memulihkan perilaku pra-iOS13 tanpa memperbarui setiap baris kode.
bevoy