通用全局优雅方式将条形按钮项添加到项目的任何UIViewController

时间:2017-09-14 14:51:26

标签: ios swift uinavigationcontroller uibarbuttonitem uinavigationitem

通常我们在项目中有一组预定义的UIBarButtonItem可以在项目中使用,多次像左侧菜单按钮一样打开侧边菜单,它可以在不同的UIViewController中使用s也是一个关闭按钮,关闭呈现的视图控制器。

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

我提出了一种方法,但它远非完美:

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

我的方法的局限性是:

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

  • 该怎么办?
  • 它假设您只想展示UIViewController

  • 不优雅。

我确信Swift语言和面向协议的编程有更好的方法可以实现预期的结果,更灵活,更有想法吗?

1 个答案:

答案 0 :(得分:1)

看起来你有一个默认的条形按钮配置,但具体(到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:
    }
}