メソッドswizzlingを使用して、すべてのUIViewControllerインスタンスのiOS13でmodalPresentationStyleを一度に変更します。


11

[Q&A] iOS 13でグローバルに値を変更UIViewController.modalPresentationStyleして、iOS 12(またはそれ以前)と同じように動作させることはできますか?


どうして?

iOS 13 SDKでは、UIViewController.modalPresentationStyleプロパティのデフォルト値がからに変更されUIModalPresentationFullScreenましUIModalPresentationAutomaticた。私の知る限り、UIModalPresentationPageSheetiOSデバイスまたは少なくともiPhone では解決されています。

私が数年取り組んできたプロジェクトはかなり大きくなったので、ビューコントローラが表示される場所は数十か所あります。新しいプレゼンテーションスタイルは、常にアプリのデザインと一致しているわけではなく、UIが崩れる場合があります。、我々は変更することdeciced理由であるUIViewController.modalPresentationStyleに戻ってUIModalPresentationFullScreen、それがあったように、プリiOS13 SDKのバージョンです。

しかし、コントローラーが提示されているすべての場所でviewController.modalPresentationStyle = UIModalPresentationFullScreen呼び出す前に追加することpresentViewController:animated:completion:は、やり過ぎのように見えました。さらに、そのときはもっと深刻な問題に対処する必要がありました。そのため、当面、または少なくともデザインを更新してすべてのUIの問題を修正するまで、メソッドスウィズリングアプローチを採用することにしました。

私の答えには実用的な解決策が示されていますが、そのようなアプローチの欠点や結果が何かということを教えてくれるフィードバックをいただければ幸いです。

回答:


12

メソッドのスウィズリングを使用してこれを実現した方法を次に示します。


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:呼び出しによってスウィズリングを呼び出します(Swiftバージョンのみ)。

UIViewController.swizzlePresentationStyle()

それが1回だけ呼び出されることを確認してください(使用dispatch_onceまたは同等のもの)。


ここでメソッドのスウィズリングの詳細:


1
1つの問題は、iPadで実行していて、実際にはフルスクリーンではなくページシートが必要な場合です。チェックを更新して自動を全画面表示に変更し、表示するビューコントローラーにコンパクトな幅の特性がある場合にのみ変更することをお勧めします。
rmaddy

このソリューションは良いですか?誰かが本当に.pageSheetとしてViewControllerを提示したい場合はどうなりますか?
ibrahimyilmaz

1
@ibrahimyilmaz次に、に設定viewController.modalPresentationStyle.pageSheetて呼び出しますself.swizzled_present(:,:,:)。多分それはそれほどきれいではないかもしれませんが、この投稿の全体のポイントは、すでにモーダルプレゼンテーションの呼び出しが多数ある既存のプロジェクトがあり、コードの各行を更新せずにiOS13以前の動作を復元したいという仮定に基づいています。
bevoy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.