Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Add same navigation bar items to every page

Tags:

ios

swift

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
}
like image 828
Dennis van Mazijk Avatar asked Nov 24 '16 08:11

Dennis van Mazijk


1 Answers

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.

like image 125
Jacob King Avatar answered Sep 27 '22 21:09

Jacob King