使用swizzling方法在所有UIViewController实例上一次更改iOS13上的modalPresentationStyle


11

[问与答] 是否可以UIViewController.modalPresentationStyle在iOS 13上全局更改值,使其表现得像在iOS 12(或更早版本)上一样?


为什么?

在iOS版SDK 13的默认值UIViewController.modalPresentationStyle属性已经从改变UIModalPresentationFullScreenUIModalPresentationAutomatic是,据我所知,解决了UIModalPresentationPageSheet在iOS设备上,或者至少在iPhone上。

由于我已经工作了几年的项目变得非常庞大,因此在很多地方都提供了视图控制器。新的演示样式并不总是与我们的应用程序设计匹配,有时会导致UI崩溃。这就是为什么我们决定改UIViewController.modalPresentationStyleUIModalPresentationFullScreen为iOS13 SDK之前的版本。

但是,viewController.modalPresentationStyle = UIModalPresentationFullScreen在调用presentViewController:animated:completion:控制器的每个位置进行调用之前添加似乎太过分了。而且,那时我们还有更严重的事情要处理,这就是为什么暂时或至少在我们更新设计并解决所有UI问题之前,我们决定采用方法混淆方法的原因。

我的答案中给出了有效的解决方案,但是希望您能收到任何反馈,告诉我这种方法可能带来的不利影响或后果。

Answers:


12

这是我们通过使用swizzling方法实现的:


目标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

迅速

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

在中AppDelegateapplication:didFinishLaunchingWithOptions:通过调用来调用swizzling(仅适用于swift版本):

UIViewController.swizzlePresentationStyle()

确保只调用一次(使用dispatch_once或等效命令)。


有关方法的更多信息,请点击此处:


1
一个问题可能是在iPad上运行,而您实际上确实希望使用页面而不是全屏显示。您可能希望将检查更新为仅自动更改为全屏,并且仅在呈现的视图控制器具有紧凑的宽度特征时才执行。
rmaddy19,19年

这个解决方案好吗?如果有人真的想将ViewController呈现为.pageSheet怎么办?
ibrahimyilmaz

1
@ibrahimyilmaz然后您设置viewController.modalPresentationStyle.pageSheet并致电self.swizzled_present(:,:,:)。也许不是很漂亮,但是本文的重点是基于这样一个假设:您已经有一个存在的项目,需要进行模式表示的大量调用,并且您想要在不更新每一行代码的情况下还原iOS13之前的行为。
bevoy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.