Установите глобальный режим отображения кнопки возврата по умолчанию для всех экземпляров UIViewController

#ios #swift #back-button #ios14 #back-button-control

#iOS #swift #кнопка возврата #ios14 #кнопка возврата-управление

Вопрос:

В iOS 14 введен настраиваемый режим кнопки возврата. Например. у вас может быть текст метки «Назад» на кнопке, но в меню компактной истории вы все равно можете видеть правильное название предыдущих контроллеров.

Я ищу простой и приятный способ настройки режима по умолчанию, чтобы все экземпляры UIViewController во время выполнения имели установленный режим по умолчанию, например UINavigationItemBackButtonDisplayModeGeneric

Интересно, есть ли способ сделать это без подкласса UIViewController или не забывайте всегда настраивать вручную каждый экземпляр UIViewController (через viewController.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeGeneric ).

Любой удобный метод, который не требует обширного рефакторинга сотен экземпляров UIViewController, с благодарностью!

Ответ №1:

Без подклассов, я думаю, это невозможно, поскольку navigationItem требуется экземпляр для работы и не может быть изменен непосредственно из расширений

 class GenericViewController: UIViewController {
override func viewDidLoad() {
    super.viewDidLoad()
    // your code here  
    viewController.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeGeneric
  }
}
  

И используйте это там, где вам нужно

 class viewController: GenericViewController
  

Это действительно хороший подход, поскольку у вас есть контроль над тем, что его реализует, а что нет, учитывая, что его может не быть во всех сценах

Ответ №2:

Чтобы решить ту же проблему, я использовал метод swizzling

 import UIKit

private let swizzling: (UIViewController.Type, Selector, Selector) -> Void = { forClass, originalSelector, swizzledSelector in
    if let originalMethod = class_getInstanceMethod(forClass, originalSelector), let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
        let didAddMethod = class_addMethod(forClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
        if didAddMethod {
            class_replaceMethod(forClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}

extension UIViewController {
    
    static func swizzle() {
        let originalSelector1 = #selector(viewDidLoad)
        let swizzledSelector1 = #selector(swizzled_viewDidLoad)
        swizzling(UIViewController.self, originalSelector1, swizzledSelector1)
    }
    
    @objc open func swizzled_viewDidLoad() {
        if let _ = navigationController {
            if #available(iOS 14.0, *) {
                navigationItem.backButtonDisplayMode = .generic
            } else {
                // Fallback on earlier versions
                navigationItem.backButtonTitle = "Back"
            }
        }
        swizzled_viewDidLoad()
    }
}
  

И в application(_:didFinishLaunchingWithOptions:) вызове

 UIViewController.swizzle()