Swift - Add Same Navigation Bar Items to Every Page

Add same NavigationBar on every page in swift

From the looks of the code, he says you would need to declare each ViewController. In his case, when he taps the open search button the code would execute the open search function, so from the looks of it it would not create infinite amounts of navigation bars. Remember in the future after implementing your code you can always build and run your application to test things like these to make sure I’m right. It never hurts to try! - Colin

Swift - Add same navigation bar items to every page

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.

How do you add a bar button item to all View Controllers in a Navigation Controller

There's a couple ways you could do it.


Make the SceneDelegate the delegate of the navigation controller then add the button to ever view controller shown.

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Exit", style: .plain, target: self, action: #selector(exitPressed))
}

Make a view controller subclass that just sets the right bar button item. Then each screen that needs the button can subclass it.

class ExitButtonViewController: UIViewController {
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Exit", style: .plain, target: self, action: #selector(exitPressed))
}

func exitPressed() {
// add a common implementation or require the subclass to add one.
}
}

Make a protocol that can simplify adding the button in multiple view controllers.

protocol Exitable: NSObjectProtocol {
func exitPressed()
}

extension Exitable where Self: UIViewController {
func setupExitButton() {
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Exit", style: .plain, target: self, action: #selector(exitPressed))
}
}

class SomeViewController: UIViewController, Exitable {
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setupExitButton()
}

func exitTapped() {
// Do something
}
}

add navigation bar, and tab bar to all pages

You have to embed in your root view controller with anavigation controller, and that navigation controller embed in with a tab bar controller.

To "embed in" a view controller, just select your root view controller on thestoryboard and select Editor -> Embed in -> Navigation Controller.

To "embed in" your navigation controller, just select it on the storyboardand select Editor -> Embed in -> Tab Bar Controller

Adding navigation bar on all the views. Swift

Yes you will have to manually add the custom bar to each of them.

However, you can make it cleaner by defining a class for that custom view. For example HeaderBar then you do all the setup in this class itself (HeaderBar.swift). When you add this bar to any view controller, all you need to do is simply drag and drop from storyboard and typing class name HeaderBar and it is ready to go, no more further configuration.

ENSideSwiftMenu: how to use same Navigation Bar for all View Controllers

You need to put a UIBarButtonItem on the navigation bar in every view controller, not in the root view controller.

Swift - How to set a different nav bar for each view?

In each view controller you can use self.navigationItem to make those changes individually.

For example, say you have a view controller names "VC1" and you want to have an add button in the navigation bar. In VC1, override viewDidLoad and do the following:

override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "addFunc")
}

UPDATE:
So, looking at your tutorial I realized that you're not pushing the child view controllers to the navigation controller, you're just adding them as child. There is a difference. Every navigation controller has a set of view controllers (you can access this: navController.viewControllers)

You can add child view controllers but it would not be any different from other controllers, if you want to actually use a navigation controller, you need to push them to the navigation controller. Otherwise, you can't access navigationItem or similar features like that.

Instead of adding the view controllers to the scroll view, use this:

self.pushViewController(childViewController1, animated: true)

If you absolutely want to have it in the scroll view AND have different navigation bar buttons for every view controller, you'd have to implement this mechanism yourself. For example, check out this:

https://github.com/peymanmortazavi/UISwipeViewController

It's not polished, you'd have to implement the layout constraints properly but it demonstrates what I mean.

Is there a way to keep the navigation bar across multiple views?

Yes. There is.

1. Create PageViewController.swift and paste the code below there

//
// Copyright © 2018 fewlinesofcode.com All rights reserved.
//
import UIKit

class PageViewController: UIPageViewController {
fileprivate(set) var currentPageIndex: Int = 0

var pages = [UIViewController]()

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

if let firstViewController = page(at: currentPageIndex) {
setViewControllers([firstViewController], direction: .forward, animated: false, completion: { completed in })
}
}

func resetIndex() { currentPageIndex = 0 }

func page(at index: Int) -> UIViewController? {
guard index >= 0 && index < pages.count else { return nil }
return pages[index]
}
}

extension PageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let pageIndex = pages.index(of: viewController), currentPageIndex != 0 else {
return nil
}
currentPageIndex = pageIndex
currentPageIndex -= 1
return page(at: currentPageIndex)
}

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let pageIndex = pages.index(of: viewController) else {
return nil
}
currentPageIndex = pageIndex + 1

if currentPageIndex == pages.count {
return nil
}
return page(at: currentPageIndex)
}

func presentationCount(for pageViewController: UIPageViewController) -> Int {
return pages.count
}

func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return currentPageIndex
}
}

extension PageViewController {
func showPrev(completion: ((Bool) -> Void)? = nil) {
guard currentPageIndex > 0 else { return }
currentPageIndex -= 1
setViewControllers([pages[currentPageIndex]], direction: .reverse, animated: true, completion: completion)
}

func showNext(completion: ((Bool) -> Void)? = nil) {
guard currentPageIndex < pages.count - 1 else { return }
currentPageIndex += 1
setViewControllers([pages[currentPageIndex]], direction: .forward, animated: true, completion: completion)
}
}

2. Subclass it like in the sample below:

class ViewController: PageViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

setupChildViewControllers()
}

private func setupChildViewControllers() {
let vc1 = <Instantiate your VC 1>
let vc2 = <Instantiate your VC 2>

pages = [
vc1, vc2
]
}
}

3. Setup navigation

Just embed ViewController in UINavigationController.



Related Topics



Leave a reply



Submit