Yöntem swizzling özelliğini kullanarak tüm UIViewController örneklerinde iOS13'te modalPresentationStyle öğesini bir kerede değiştirin


11

[Soru ve Cevap] iOS 13'te (veya daha önce) eskisi gibi davranması için iOS 13'te genel olarak değer değiştirmek mümkün müdür UIViewController.modalPresentationStyle?


Neden?

İOS 13 SDK ise varsayılan değeri UIViewController.modalPresentationStyleözelliğinden değişmiştir UIModalPresentationFullScreeniçin UIModalPresentationAutomaticolduğu gibi bildiğim kadarıyla, karar, hangi UIModalPresentationPageSheetiOS cihazlarda veya iPhone'larda en azından.

Birkaç yıldır çalıştığım proje oldukça büyük olduğundan, bir görünüm denetleyicisinin sunulduğu onlarca yer var. Yeni sunum stili her zaman uygulama tasarımlarımızla eşleşmez ve bazen kullanıcı arayüzünün dağılmasına neden olur. Bu nedenle, iOS13 öncesi SDK'nın sürümleri olduğu gibi UIViewController.modalPresentationStylegeri dönmeye karar UIModalPresentationFullScreenverdik.

Ancak , bir denetleyicinin sunulduğu her yere viewController.modalPresentationStyle = UIModalPresentationFullScreenaramadan önce eklemek presentViewController:animated:completion:, aşırıya kaçmış gibi görünüyordu. Dahası, o zaman uğraşmak için daha ciddi meselelerimiz vardı, bu yüzden şu an için veya en azından tasarımlarımızı güncelleyip tüm UI sorunlarını düzeltene kadar yöntem swizzling yaklaşımına gitmeye karar verdik.

Çalışma çözümü cevabımda sunuldu, ancak böyle bir yaklaşımın ne gibi olumsuz veya sonuçlarının olabileceğini söyleyen herhangi bir geri bildirimi takdir ediyorum.

Yanıtlar:


12

Yöntem swizzling'i kullanarak bunu nasıl başardık:


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

hızlı

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

ve içinde AppDelegate, application:didFinishLaunchingWithOptions:arayarak swizzling'i çağırın (yalnızca hızlı sürüm):

UIViewController.swizzlePresentationStyle()

Sadece bir kez çağrıldığından emin olun (kullanın dispatch_onceveya eşdeğeri).


Burada yöntem swizzling hakkında daha fazla bilgi:


1
Bir sorun, tam ekran değil, bir sayfa sayfası olmasını istediğiniz bir iPad'de çalışıyor olabilir. Yalnızca otomatik olarak tam ekrana geçmek için kontrolünüzü güncellemek isteyebilirsiniz ve bunu yalnızca sunum görünümü denetleyicisinin kompakt genişlik özellikleri olduğunda yapabilirsiniz.
rmaddy

Bu çözüm iyi mi? Birisi gerçekten bir ViewController'ı .pageSheet olarak sunmak istiyorsa ne olur?
ibrahimyilmaz

1
@ibrahimyilmaz ardından ayarlamak viewController.modalPresentationStyleiçin .pageSheetve çağrı self.swizzled_present(:,:,:). Belki de çok hoş değil, ancak bu yazının tüm konusu, modal sunum için çok sayıda çağrı içeren geniş bir projeye sahip olduğunuz ve her kod satırını güncellemeden iOS13 öncesi davranışı geri yüklemek istediğiniz varsayımına dayanıyordu.
bevoy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.