使用方法调配一次在所有 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
在 AppDelegate
和 application: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的主要内容,如果未能解决你的问题,请参考以下文章
如何将randomforest分类器应用于所有数据集,一次在python中使用一小部分