เปลี่ยน modalPresentationStyle บน iOS13 บนอินสแตนซ์ UIViewController ทั้งหมดในคราวเดียวโดยใช้วิธีการที่รวดเร็ว


11

[คำถาม & คำตอบ] เป็นไปได้ไหมที่จะเปลี่ยนUIViewController.modalPresentationStyleค่าทั่วโลกใน iOS 13 ดังนั้นมันจะทำงานเหมือนที่เคยเป็นใน iOS 12 (หรือเก่ากว่า)?


ทำไม?

ใน iOS SDK 13 ค่าเริ่มต้นของUIViewController.modalPresentationStyleสถานที่ให้บริการมีการเปลี่ยนแปลงจากUIModalPresentationFullScreenไปUIModalPresentationAutomaticซึ่งก็คือเท่าที่ผมรู้ว่ามีมติUIModalPresentationPageSheetในอุปกรณ์ iOS หรืออย่างน้อยบน iPhone

เนื่องจากโครงการที่ฉันทำงานมาหลายปีมีขนาดค่อนข้างใหญ่จึงมีสถานที่นับสิบแห่งที่นำเสนอตัวควบคุมมุมมอง สไตล์การนำเสนอใหม่ไม่ตรงกับการออกแบบแอพของเราเสมอไปและบางครั้งก็ทำให้ UI ขาดหายไป นี่คือเหตุผลที่เราตัดสินใจเปลี่ยนUIViewController.modalPresentationStyleกลับไปUIModalPresentationFullScreenเป็นรุ่นก่อนหน้าของ iOS13 SDK

แต่การเพิ่มviewController.modalPresentationStyle = UIModalPresentationFullScreenก่อนโทรpresentViewController:animated:completion:ในทุก ๆ สถานที่ที่มีการนำเสนอตัวควบคุมดูเหมือน overkill ยิ่งไปกว่านั้นเรามีเรื่องที่ต้องทำอย่างจริงจังมากขึ้นในเวลานั้นซึ่งเป็นเหตุผลว่าทำไมในขณะนี้หรืออย่างน้อยก็จนกว่าเราจะอัพเดตการออกแบบและแก้ไขปัญหา UI ทั้งหมดเราตัดสินใจที่จะใช้วิธีการแบบว่องไว

วิธีแก้ไขปัญหาการทำงานนั้นถูกนำเสนอในคำตอบของฉัน แต่ฉันจะขอบคุณข้อเสนอแนะใด ๆ ที่บอกฉันว่าสิ่งที่ผิดพลาดหรือผลที่ตามมาของวิธีการดังกล่าวอาจจะเป็น

คำตอบ:


12

นี่คือวิธีการที่เราประสบความสำเร็จโดยใช้วิธีการแบบ swizzling:


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 โดยการเรียก (รุ่นที่รวดเร็วเท่านั้น):

UIViewController.swizzlePresentationStyle()

ตรวจสอบให้แน่ใจว่ามันถูกเรียกเพียงครั้งเดียว (ใช้dispatch_onceหรือเทียบเท่าบางส่วน)


เพิ่มเติมเกี่ยวกับวิธีการที่นี่อย่างรวดเร็ว:


1
ปัญหาหนึ่งจะเกิดขึ้นบน iPad ที่คุณต้องการแผ่นชีทจริงไม่ใช่เต็มหน้าจอ คุณอาจต้องการอัปเดตเช็คของคุณเพื่อเปลี่ยนอัตโนมัติเป็นแบบเต็มหน้าจอและทำเฉพาะเมื่อตัวควบคุมมุมมองการนำเสนอมีลักษณะความกว้างขนาดกะทัดรัด
rmaddy

วิธีนี้ดีหรือไม่? จะเป็นอย่างไรถ้ามีคนต้องการเสนอ ViewController เป็น. pageSheet
ibrahimyilmaz

1
@ibrahimyilmaz นั้นคุณตั้งค่าviewController.modalPresentationStyleการโทร.pageSheet self.swizzled_present(:,:,:)อาจจะไม่สวยมาก แต่จุดทั้งหมดของโพสต์นี้เป็นไปตามข้อสันนิษฐานที่ว่าคุณมีโครงการขยายที่มีการเรียกใช้งานนำเสนอเป็นจำนวนมากอยู่แล้วและคุณต้องการกู้คืนพฤติกรรมก่อน iOS13 โดยไม่ต้องอัปเดตโค้ดแต่ละบรรทัด
bevoy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.