Thay đổi modalPftimeationStyle trên iOS13 trên tất cả các phiên bản UIViewControll cùng một lúc bằng cách sử dụng phương thức swizzling


11

[Q & A] Có thể thay đổi UIViewController.modalPresentationStylegiá trị trên toàn cầu trên iOS 13 để nó hoạt động như trước đây trên iOS 12 (hoặc trước đó) không?


Tại sao?

Trong iOS 13 SDK giá trị mặc định của UIViewController.modalPresentationStylebất động sản đã được thay đổi từ UIModalPresentationFullScreentới UIModalPresentationAutomaticđó là, theo như tôi biết, quyết tâm UIModalPresentationPageSheettrên các thiết bị iOS hoặc ít nhất trên iPhone.

Vì dự án tôi đã làm việc trong nhiều năm đã trở nên khá lớn, có hàng chục nơi trình bày bộ điều khiển xem. Kiểu trình bày mới không phải lúc nào cũng phù hợp với thiết kế ứng dụng của chúng tôi và đôi khi nó khiến giao diện người dùng bị sụp đổ. Đó là lý do tại sao, chúng tôi quyết định thay đổi UIViewController.modalPresentationStyletrở lại UIModalPresentationFullScreenvì đây là phiên bản SDK của iOS13 trước.

Nhưng việc thêm viewController.modalPresentationStyle = UIModalPresentationFullScreentrước khi gọi presentViewController:animated:completion:ở mọi nơi duy nhất có trình điều khiển được trình bày có vẻ như quá mức cần thiết. Hơn nữa, chúng tôi có nhiều vấn đề nghiêm trọng hơn để giải quyết vào thời điểm đó, đó là lý do tại sao, trong thời gian hiện tại hoặc ít nhất là cho đến khi chúng tôi cập nhật thiết kế của mình và khắc phục tất cả các vấn đề về UI, chúng tôi đã quyết định áp dụng phương pháp tiếp cận phương pháp.

Giải pháp làm việc được trình bày trong câu trả lời của tôi, nhưng tôi sẽ đánh giá cao bất kỳ phản hồi nào cho tôi biết những nhược điểm hoặc hậu quả của cách tiếp cận như vậy có thể là gì.

Câu trả lời:


12

Đây là cách chúng tôi đạt được điều đó bằng cách sử dụng phương pháp swizzling:


Mục tiêu-C

UIViewControll + iOS13Fixes.h

#import <Foundation/Foundation.h>

@interface UIViewController (iOS13Fixes)
@end

UIViewControll + 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

Nhanh

UIViewControll + 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
    }
}

và trong AppDelegate, application:didFinishLaunchingWithOptions:gọi ra sự thay đổi bằng cách gọi (chỉ phiên bản nhanh):

UIViewController.swizzlePresentationStyle()

Hãy chắc chắn rằng nó chỉ được gọi một lần (sử dụng dispatch_oncehoặc một số tương đương).


Thêm về phương pháp swizzling ở đây:


1
Một vấn đề sẽ xảy ra trên iPad khi bạn thực sự muốn có một trang và không toàn màn hình. Bạn có thể muốn cập nhật kiểm tra của mình để chỉ thay đổi tự động thành toàn màn hình và chỉ thực hiện khi trình điều khiển chế độ xem trình bày có các đặc điểm chiều rộng nhỏ gọn.
rmaddy

Giải pháp này có tốt không? Điều gì xảy ra nếu ai đó thực sự muốn trình bày ViewContoder dưới dạng .pageSheet?
ibrahimyilmaz

1
@ibrahimyilmaz sau đó bạn thiết lập viewController.modalPresentationStyleđể .pageSheetvà cuộc gọi self.swizzled_present(:,:,:). Có thể nó không đẹp lắm, nhưng toàn bộ quan điểm của bài đăng này dựa trên giả định rằng bạn đã có một dự án hiện có với rất nhiều cuộc gọi để trình bày phương thức và bạn muốn khôi phục hành vi trước iOS13 mà không cập nhật từng dòng mã.
bevoy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.