通用全球优雅的方式栏按钮项添加到项目
问题描述:
通常我们有一组预定义的项目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:
}
}