Хобрук: Ваш путь к мастерству в программировании

Измените modalPresentationStyle на iOS13 сразу на всех экземплярах UIViewController, используя метод swizzling

[Q&A] Можно ли глобально изменить значение UIViewController.modalPresentationStyle в iOS 13, чтобы оно работало так же, как в iOS 12 (или более ранней версии)?


Почему?

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

Поскольку проект, над которым я работаю несколько лет, стал довольно большим, есть десятки мест, где представлен контроллер представления. Новый стиль презентации не всегда соответствует дизайну нашего приложения, а иногда и приводит к разложению пользовательского интерфейса. Вот почему мы решили изменить UIViewController.modalPresentationStyle обратно на UIModalPresentationFullScreen, как это было в версиях SDK до iOS13.

Но добавление viewController.modalPresentationStyle = UIModalPresentationFullScreen перед вызовом presentViewController:animated:completion: в каждом месте, где представлен контроллер, казалось излишним. Более того, в то время у нас были более серьезные вопросы, поэтому на данный момент или, по крайней мере, до тех пор, пока мы не обновим наши дизайны и не исправим все проблемы пользовательского интерфейса, мы решили пойти на смену методов.

В моем ответе представлено рабочее решение, но я буду признателен за любые отзывы, которые расскажут мне, какие могут быть недостатки или последствия такого подхода.

03.10.2019

Ответы:


1

Вот как мы достигли этого с помощью переключения методов:


Цель-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

Swift

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: вызвать swizzling путем вызова (только для быстрой версии):

UIViewController.swizzlePresentationStyle()

Убедитесь, что он вызывается только один раз (используйте dispatch_once или аналогичный).


Подробнее о методе swizzling здесь:

03.10.2019
  • Одна из проблем могла бы быть запущена на iPad, где вам действительно нужен лист страницы, а не полноэкранный режим. Возможно, вы захотите обновить свой чек, чтобы он изменял только автоматический на полноэкранный режим и делал это только тогда, когда представляющий контроллер представления имеет компактные характеристики ширины. 03.10.2019
  • Это хорошее решение? Что, если кто-то действительно хочет представить ViewController как .pageSheet? 18.11.2019
  • @ibrahimyilmaz, затем вы устанавливаете viewController.modalPresentationStyle на .pageSheet и вызываете self.swizzled_present(:,:,:). Может быть, это не очень красиво, но вся суть этого поста основана на предположении, что у вас уже есть существующий проект с множеством вызовов модальной презентации, и вы хотите восстановить поведение до iOS13, не обновляя каждую строку кода. 18.11.2019
  • Новые материалы

    Еженедельный выпуск React 75
    Добро пожаловать в 75-й выпуск React Weekly, еженедельный обзор последних ссылок и руководств по React и React Native. Посетите Купить кофе , чтобы найти способы поддержать React Weekly, или..

    (Почти) полнофункциональная реализация Gumbel MuZero в Джулии.
    TLDR; Gumbel Muzero — это современный алгоритм обучения с подкреплением. Он достиг сверхчеловеческой производительности во многих настольных играх и даже, что более впечатляюще, в Atari..

    Передача нейронного стиля в браузерах с использованием Angular и WebDNN
    В последние годы глубокое обучение привлекло большое внимание из-за его безупречной производительности при обучении на обширных данных и высокой точности при выводе. Развертывание этих алгоритмов..

    Шифр Цезаря в C
    Шифр Цезаря  — один из самых простых и широко известных методов шифрования. Он включает в себя сдвиг каждой буквы в сообщении на определенное количество позиций в алфавите. В этой статье мы..

    Исследовательский анализ данных (EDA)
    Что такое ЭДА? Анализ данных, который ищет закономерности в данных, известен как исследовательский анализ данных. Это похоже на беглый просмотр данных, чтобы найти наиболее важные..

    1x1 Convolution: демистификация
    Чтобы пролить свет на концепцию операции свертки 1x1, которая представлена ​​в статье «Сеть в сети» Лин и др. и Google Inception Некоторое время назад я прочитал статью «Сеть в сети» (NiN),..

    Лучшие расширения VS Code в 2022 году
    Код ВС Лучшие расширения VS Code в 2022 году Список самых полезных расширений VS Code, которые вы можете использовать как разработчик в 2022 году Расширения находятся на рынке VS Code,..