ios - 使用方法调配立即更改所有 UIViewController 实例上 iOS13 上的 modalPresentationStyle

标签 ios objective-c swift ios13

[Q&A] 是否可以在 iOS 13 上全局更改 UIViewController.modalPresentationStyle 值,使其行为与 iOS 12(或更早版本)上的行为类似?

<小时/>

为什么?

在 iOS 13 SDK 中,UIViewController.modalPresentationStyle 属性的默认值已从 UIModalPresentationFullScreen 更改为 UIModalPresentationAutomatic,据我所知知道,在 iOS 设备或至少在 iPhone 上解析为 UIModalPresentationPageSheet

由于我工作了几年的项目已经变得相当大,有数十个地方呈现了 View Controller 。新的呈现风格并不总是与我们的应用程序设计相匹配,有时它会导致用户界面崩溃。这就是为什么,我们决定将 UIViewController.modalPresentationStyle 更改回 UIModalPresentationFullScreen,因为它是 iOS13 之前的 SDK 版本。

但是在呈现 Controller 的每个地方调用 presentViewController:animated:completion: 之前添加 viewController.modalPresentationStyle = UIModalPresentationFullScreen 似乎有些过分了。此外,当时我们有更严重的事情需要处理,这就是为什么,暂时或至少在我们更新设计并解决所有 UI 问题之前,我们决定采用方法调配方法。

我的回答中提出了可行的解决方案,但如果有任何反馈告诉我这种方法可能有哪些缺点或后果,我将不胜感激。

最佳答案

以下是我们如何通过使用方法调配来实现这一目标:

<小时/>

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
<小时/>

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:中通过调用(仅限swift版本)来调用swizzling:

UIViewController.swizzlePresentationStyle()

确保仅调用一次(使用 dispatch_once 或其他类似方法)。

<小时/>

有关方法调配的更多信息请参见此处:

关于ios - 使用方法调配立即更改所有 UIViewController 实例上 iOS13 上的 modalPresentationStyle,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58219138/

相关文章:

ios - zbar #ifdef 问题与 iOS 中的 minizip

ios - 在运行时在 iPhone 的核心数据模型中以编程方式创建实体

ios - NSDecimalNumber 计算错误

ios - 在游戏场景中隐藏 iAd 横幅?

ios - 设置导航栏颜色

swift - swift 3 中 'continue' Bolts-Swift 的使用不明确

iphone - 用户创建表单,如 iOS 联系人创建

ios - 是否有任何公共(public) api 来检测 AirPlay 是否可用

iphone - Objective-c/IOS : Reversing the bytes in NSMutabledata

swift - 从 ARKit 场景获取 ARFrame