【问题标题】:Generic Global elegant way to add bar button items to any UIViewController of the project将条形按钮项添加到项目的任何 UIViewController 的通用全局优雅方式
【发布时间】:2018-02-23 14:55:55
【问题描述】:

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

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

我想出了一个方法,但它远非完美:

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

我的方法的局限性是:

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

  • 假设您只想展示UIViewController

  • 不优雅。

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

【问题讨论】:

    标签: ios swift uinavigationcontroller uibarbuttonitem uinavigationitem


    【解决方案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:
        }
    }
    

    【讨论】:

    • 已经晚了,但我使用你的方法,我在左边添加了一个配置文件按钮并通过 sdWebImage 加载配置文件图像,现在当用户按下帐户页面上的注销按钮时,我需要重置配置文件按钮图片?
    • 迄今为止我见过的最好的方法。我在我的项目中使用
    猜你喜欢
    • 2010-11-13
    • 2016-11-03
    • 1970-01-01
    • 2014-04-30
    • 1970-01-01
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 2014-09-01
    相关资源
    最近更新 更多