Generic Global forma elegante de agregar elementos de button de barra a cualquier UIViewController del proyecto

Por lo general, tenemos un set pnetworkingefinido de UIBarButtonItem en el proyecto que se puede utilizar en el proyecto y varias veces, como un button de menu izquierdo para abrir un menu lateral, se puede usar en diferentes UIViewController , también un button de cierre que descarta el controller de vista presentado .

La forma clásica es agregar estos botones según sea necesario, pero esto introduce una duplicación de código y todos queremos evitarlo.

Mi propuesta es aproximada, pero está lejos de ser perfecta:

 enum BarButtonItemType { case menu, close, notification } enum BarButtonItemPosition{ case right, left } extension UIViewController { func add(barButtons:[BarButtonItemType], position: BarButtonItemPosition) { let barButtonItems = barButtons.map { rightBarButtonType -> UIBarButtonItem in switch rightBarButtonType { case .menu: return UIBarButtonItem(image: UIImage(named:"menu"), style: .plain, target: self, action: #selector(presentLeftMenu(_:))) case .notification: return UIBarButtonItem(image: UIImage(named:"notification"), style: .plain, target: self, action: #selector(showNotification(_:))) case .close: return UIBarButtonItem(image: UIImage(named:"close"), style: .plain, target: self, action: #selector(dismissController(_:))) } } switch position { case .right: self.navigationItem.rightBarButtonItems = barButtonItems case .left: self.navigationItem.leftBarButtonItems = barButtonItems } } // MARK: Actions @objc fileprivate func presentLeftMenu(_ sender:AnyObject) { self.parent?.presentLeftMenuViewController(sender) } @objc fileprivate func dismissController(_ sender:AnyObject) { self.dismiss(animated: true, completion: nil) } @objc fileprivate func showNotification(_ sender:AnyObject) { let notificationViewController = UINavigationController(rootViewController:NotificationViewController()) self.present(notificationViewController, animated: true, completion: nil) } } 

y luego el uso:

 override func viewDidLoad() { super.viewDidLoad() self.add(barButtons: [.close], position: .right) self.add(barButtons: [.menu], position: .left) } 

Las limitaciones de mi enfoque son:

  • La extensión necesita saber cómo instanciar un nuevo controller de vista (caso de notificación, por ejemplo) y qué ocurre si viewController debe estar equipado con parameters

  • Asume que solo desea presentar un UIViewController

  • No elegante

Estoy seguro de que hay una mejor manera con el lenguaje Swift y la progtwigción orientada al protocolo que puede lograr el resultado deseado con más flexibilidad, ¿alguna idea?

Parece que estás después de tener una configuration de button de barra pnetworkingeterminada pero implementaciones de acción de button de barra específica (a subclass de UIViewController). Mencionó 1. "La extensión necesita saber cómo instanciar el nuevo controller de vista" y su segundo punto 2. "Asume que solo desea presentar un UIViewController", eso es una buena señal de que su extensión debe delegar ese trabajo en una subclass que sabe qué hacer con esas acciones. Aquí he hecho una implementación de ejemplo:

 enum BarButtonItemPosition { case right, left } enum BarButtonItemType { case menu(BarButtonItemPosition) case close(BarButtonItemPosition) case notification(BarButtonItemPosition) } /// Has default implementation on UIViewControllers that conform to BarButtonActions. protocol BarButtonItemConfiguration: class { func addBarButtonItem(ofType type: BarButtonItemType) } /// Hate that we're forced to expose button targets to objc runtime :( /// but I don't know any other way for the time being, maybe in Swift 6 :) @objc protocol BarButtonActions { @objc func presentLeftMenu(_ sender:AnyObject) @objc func dismissController(_ sender:AnyObject) @objc func showNotification(_ sender:AnyObject) } extension BarButtonItemConfiguration where Self: UIViewController, Self: BarButtonActions { func addBarButtonItem(ofType type: BarButtonItemType) { func newButton(imageName: String, position: BarButtonItemPosition, action: Selector?) { let button = UIBarButtonItem(image: UIImage(named: imageName), style: .plain, target: self, action: action) switch position { case .left: self.navigationItem.leftBarButtonItem = button case .right: self.navigationItem.rightBarButtonItem = button } } switch type { case .menu(let p): newButton(imageName: "", position: p, action: #selector(Self.presentLeftMenu(_:))) case .notification(let p): newButton(imageName: "", position: p, action: #selector(Self.showNotification(_:))) case .close(let p): newButton(imageName: "", position: p, action: #selector(Self.dismissController(_:))) } } } /// Conform to this in subclasses of UIViewController and implement BarButtonActions (its impl. differs from vc to vc). protocol BarButtonConfigarable: BarButtonItemConfiguration, BarButtonActions {} /// example class SampleVC: UIViewController, BarButtonConfigarable { override func viewDidLoad() { super.viewDidLoad() addBarButtonItem(ofType: .menu(.right)) addBarButtonItem(ofType: .menu(.left)) } @objc func presentLeftMenu(_ sender:AnyObject) { // TODO: } @objc func dismissController(_ sender:AnyObject) { // TODO: } @objc func showNotification(_ sender:AnyObject) { // TODO: } }