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

Posted

技术标签:

【中文标题】使用方法调配一次在所有 UIViewController 实例上更改 iOS13 上的 modalPresentationStyle【英文标题】:Change modalPresentationStyle on iOS13 on all UIViewController instances at once using method swizzling 【发布时间】:2019-10-03 12:28:41 【问题描述】:

[Q&A] 是否可以在 ios 13 上全局更改 UIViewController.modalPresentationStyle 值,使其表现得像以前在 iOS 12(或更早版本)上一样? p>


为什么?

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

自从我工作了几年的项目变得相当大以来,有几十个地方展示了视图控制器。新的演示风格并不总是与我们的应用程序设计相匹配,有时它会导致 UI 崩溃。这就是为什么我们决定将UIViewController.modalPresentationStyle 改回UIModalPresentationFullScreen,因为它是iOS13 之前的SDK 版本。

但是,在每个出现控制器的地方,在调用 presentViewController:animated:completion: 之前添加 viewController.modalPresentationStyle = UIModalPresentationFullScreen 似乎有点过头了。此外,当时我们有更严重的事情要处理,这就是为什么在目前或至少在我们更新设计并修复所有 UI 问题之前,我们决定采用方法混合的方法。

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

【问题讨论】:

【参考方案1】:

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


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

斯威夫特

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 或类似的东西)。


更多关于方法调配的信息:

https://nshipster.com/method-swizzling/ https://nshipster.com/swift-objc-runtime/ https://medium.com/rocknnull/ios-to-swizzle-or-not-to-swizzle-f8b0ed4a1ce6

【讨论】:

一个问题是在 iPad 上运行时,您确实需要一个页面而不是全屏。您可能希望更新您的检查以仅将自动更改为全屏,并且仅在呈现视图控制器具有紧凑宽度特征时才这样做。 这个解决方案好吗?如果有人真的想将 ViewController 呈现为 .pageSheet 怎么办? @ibrahimyilmaz 然后将viewController.modalPresentationStyle 设置为.pageSheet 并调用self.swizzled_present(:,:,:)。也许它不是很漂亮,但这篇文章的全部观点是基于这样一个假设,即您已经有一个现有的项目,其中有很多对模态表示的调用,并且您希望在不更新每一行代码的情况下恢复 iOS13 之前的行为。跨度>

以上是关于使用方法调配一次在所有 UIViewController 实例上更改 iOS13 上的 modalPresentationStyle的主要内容,如果未能解决你的问题,请参考以下文章

如何一次在 GitLab 中克隆用户的所有项目?

如何将randomforest分类器应用于所有数据集,一次在python中使用一小部分

sh 方便的脚本一次在所有docker容器中执行

如何一次在 R 中的插入符号中为所有算法查找算法类型(回归、分类)?

一次在Firefox中运行两个快捷命令

一次在多个表中插入/更新数据的最佳实践