메소드 스위 즐링을 사용하여 모든 UIViewController 인스턴스에서 iOS13의 modalPresentationStyle을 한 번에 변경


11

[Q & A] iOS 13에서 전 세계적으로 값 을 변경 UIViewController.modalPresentationStyle하여 iOS 12 (또는 이전)에서와 같이 동작합니까?


왜?

아이폰 OS (13) SDK에서의 기본값 UIViewController.modalPresentationStyle속성이 변경되었습니다 UIModalPresentationFullScreenUIModalPresentationAutomatic내가 아는 한에 해결, 어떤 UIModalPresentationPageSheetiOS 기기 나 아이폰에 적어도.

몇 년 동안 작업 한 프로젝트가 상당히 커지면서 뷰 컨트롤러가 제공되는 곳은 수십 곳이 있습니다. 새로운 프레젠테이션 스타일이 앱 디자인과 항상 일치하는 것은 아니며 때로는 UI가 분리되는 경우가 있습니다. 그렇기 때문에 우리는 iOS13 이전의 SDK 버전 으로 변경 UIViewController.modalPresentationStyle하기 UIModalPresentationFullScreen로 결정했습니다.

그러나 컨트롤러가 제공되는 모든 단일 장소에서 viewController.modalPresentationStyle = UIModalPresentationFullScreen전화하기 전에 추가 presentViewController:animated:completion:하는 것은 지나친 것처럼 보입니다. 또한 그 시점에서 처리해야 할 더 심각한 문제가 있었기 때문에 당분간 또는 적어도 디자인을 업데이트하고 모든 UI 문제를 해결하기 전까지는 메소드 스위 즐링 접근법을 사용하기로 결정했습니다.

작업 솔루션이 내 대답에 제시되어 있지만 그러한 접근법의 단점이나 결과가 무엇인지 알려주는 의견을 보내 주셔서 감사합니다.

답변:


12

다음은 메소드 스위 즐링을 사용하여 달성 한 방법입니다.


목표 -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:호출하여 스위 즐링을 호출하십시오.

UIViewController.swizzlePresentationStyle()

한 번만 호출하십시오 (사용 dispatch_once또는 이와 동등한 것).


방법 스위 즐링에 대한 자세한 내용은 다음과 같습니다.


1
실제로는 전체 화면이 아닌 페이지 시트를 원하는 iPad에서 한 가지 문제가 발생합니다. 자동으로 전체 화면으로 만 변경하고 프리젠 테이션보기 컨트롤러의 너비 특성이 작은 경우에만 확인하도록 업데이트를 원할 수 있습니다.
rmaddy

이 솔루션이 좋습니까? 누군가가 실제로 ViewController를 .pageSheet로 나타내려면 어떻게해야합니까?
ibrahimyilmaz

1
@ibrahimyilmaz 그런 다음 설정 viewController.modalPresentationStyle.pageSheet및 전화 self.swizzled_present(:,:,:). 어쩌면 꽤 예쁘지는 않지만이 게시물의 요점은 이미 모달 프레젠테이션을 요구하는 훌륭한 프로젝트가 있고 각 코드 줄을 업데이트하지 않고 iOS13 이전의 동작을 복원하려고한다는 가정에 근거한 것입니다.
19:14:45
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.