通用全球优雅的方式栏按钮项添加到项目

问题描述:

通常我们有一组预定义的项目UIBarButtonItem可以在项目和多次使用像左边的菜单按钮,打开一个侧面菜单中的任何的UIViewController,它可以用于不同的UIViewController也是关闭按钮,关闭所呈现的视图控制器。通用全球优雅的方式栏按钮项添加到项目

的经典方法是将这些按钮根据需要添加,但这种引进代码重复,我们都希望能够避免这种情况。

我想出了一个办法,但它是完美的远:

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

,然后使用:

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

我的方法的局限性是:

  • 扩展需要知道如何实例化新的视图控制器(例如通知的情况)以及如果视图控制器必须与参数inited

  • 它假定您只想提出一个UIViewController

  • 不优雅。

我相信,有一个与斯威夫特的语言和协议更好的方式面向对象编程,可以更灵活地实现预期的结果,有什么想法?

看来你有一个默认栏按钮配置,但具体的(以UIViewController的子类)栏按钮的操作实现后是。你提到1“的扩展需要知道如何实例化新的视图控制器”和你的第二个点2.“它假定您只想提出一个UIViewController”,那你应该扩展委派工作给子类一个好兆头知道如何处理这些行为。在这里,我做了一个简单的实现:

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