Змінити modalPresentationStyle в iOS13 на всіх екземплярах UIViewController одразу за допомогою методу swizzling


11

[Питання та відповіді] Чи можна UIViewController.modalPresentationStyleглобально змінити значення на iOS 13, щоб воно поводилось так, як раніше на iOS 12 (або раніше)?


Чому?

В IOS 13 SDK значення за замовчуванням UIViewController.modalPresentationStyleвласності було змінено UIModalPresentationFullScreenна UIModalPresentationAutomaticякий, наскільки я знаю, вирішив UIModalPresentationPageSheetна пристроях IOS або принаймні на iPhone'ов.

Оскільки проект, над яким я працював декілька років, став досить великим, є десятки місць, де представлений контролер перегляду. Новий стиль презентації не завжди відповідає нашому дизайну додатків, а іноді він спричиняє розпад інтерфейсу користувача. Саме тому, ми deciced змінити UIViewController.modalPresentationStyleназад , UIModalPresentationFullScreenяк це було в версії до iOS13 SDK в.

Але додавання viewController.modalPresentationStyle = UIModalPresentationFullScreenперед викликом presentViewController:animated:completion:у кожне місце, де представлений контролер, здавалося надмірним. Більше того, у нас на той час були вирішені більш серйозні питання, і саме тому, на даний момент або, принаймні, поки ми не оновимо свої дизайни і не виправимо всі проблеми з інтерфейсом, ми вирішили перейти до методу, що замикає.

Робоче рішення представлене у моїй відповіді, але я вдячний за будь-який зворотній зв'язок, який розповідає про те, якими можуть бути мінуси чи наслідки такого підходу.

Відповіді:


12

Ось як ми цього домоглися, використовуючи метод swizzling:


Ціль-С

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

Швидкий

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

і в AppDelegate, application:didFinishLaunchingWithOptions:викликаючи шиплячий виклик (лише швидка версія):

UIViewController.swizzlePresentationStyle()

Переконайтеся, що він називається лише один раз (використання dispatch_onceабо якийсь еквівалент).


Більше про метод шипування тут:


1
Одна з проблем буде працювати на iPad, де ви дійсно хочете аркуш сторінки, а не повний екран. Можливо, ви хочете оновити свій чек, щоб змінити автоматично лише на повний екран, і робити це лише тоді, коли контролер подання представлених даних має компактні риси ширини.
rmaddy

Чи добре це рішення? Що робити, якщо хтось дійсно хоче представити ViewController як .pageSheet?
ibrahimyilmaz

1
@ibrahimyilmaz потім встановити viewController.modalPresentationStyleна .pageSheetі виклик self.swizzled_present(:,:,:). Можливо, це не дуже красиво, але вся суть цієї публікації базувалася на припущенні, що у вас вже є існуючий проект з великою кількістю викликів для модальної презентації, і ви хочете відновити поведінку до iOS13, не оновлюючи кожен рядок коду.
бевой
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.