I am trying to add the same navigation bar items to every tab in my application. I currently have them set up correctly in my homeController, but I want to move the code to a separate file and remotely implement it wherever I want.
For example: adding a [search] icon to the left side of the navigation bar without having to use the same code in every swift file.
I don't know whether I should create an enum, protocol or class. What is the best way to do this?
let menuButton = UIButton(type: UIButtonType.system)
menuButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
menuButton.addTarget(self, action: #selector(openSearch), for: .touchUpInside)
menuButton.setImage(UIImage(named: "icon_search"), for: UIControlState())
let menuBarButtonItem = UIBarButtonItem(customView: menuButton)
navigationItem.leftBarButtonItems = [menuBarButtonItem]
func openSearch() {
// Some code
}
My recommended approach to this would be to create a base view controller class and make all your individual view controllers inheirit from this rather than directly from UIViewController
.
Whilst you could do a quick and dirty extension as Umair suggests, this isn't practical for other applications whereas a base view controller allows you to basically add functionality/customise appearance of any aspect of all view controllers within your app.
Here is some example code:
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let menuButton = UIButton(type: UIButtonType.system)
menuButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
menuButton.addTarget(self, action: #selector(openSearch), for: .touchUpInside)
menuButton.setImage(UIImage(named: "icon_search"), for: UIControlState())
let menuBarButtonItem = UIBarButtonItem(customView: menuButton)
navigationItem.leftBarButtonItems = [menuBarButtonItem
}
func openSearch() {
}
}
Then for all view controllers in your app, just make the declaration:
class SomeRandomViewController: BaseViewController { }
EDIT:
As correctly pointed out in the comments, you will have to do this in every base class. (UIViewController, UITableViewController, UITabViewController etc...) There is a way around this but it is often considered one of the dark arts of the Objective-C runtime. I am of course talking about method swizzling. The following code essentially swaps the implemetations of UIViewController
's viewWillAppear:
and a custom method. It is totally safe if done correctly, and a correct implementation is shown below.
extension UIViewController {
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
// make sure this isn't a subclass
if self !== UIViewController.self {
return
}
dispatch_once(&Static.token) {
let originalSelector = Selector("viewWillAppear:")
let swizzledSelector = Selector("extended_viewWillAppear:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
// MARK: - Method Swizzling
func extended_viewWillAppear(animated: Bool) {
self.extended_viewWillAppear(animated)
// Call your code here that you want to run for all view controllers, table view controllers, tab view controllers etc...
}
}
Some developers can't stand the above concept, and you should be aware that it could potentially change in a future release of iOS and break. That said, it won't get your app rejected as it is an actual programming technique.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With