I'm trying to create a custom navigation bar, and I'm having difficulty modifying different parts of the navigation bar. I can change the color of the background, but I can't seem to add buttons or change the title.
class CustomNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
// changing the background color works
self.navigationBar.barTintColor = UIColor.purpleColor()
// none of this works
let leftButton = UIBarButtonItem(title: "Info", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(openInfo))
self.navigationItem.leftBarButtonItem = leftButton
self.navigationItem.title = "MYTITLE"
}
}
I'm not sure if the fact that I'm trying to integrate this NavigationController with a TabBarController is affecting the way the view loads, but this custom NavigationController is being subclassed by each tab in the TabBarController.
According to UINavigationItem class reference, each view controller has its own UINavigationItem instance. "The managing UINavigationController object uses the navigation items of the topmost two view controllers to populate the navigation bar with content", which means its UIViewController's responsibility to create the navigation item content such as left bar item or title.
I can understand that you want to provide the same appearance of navigation bar throughout the app. But why do you want to set the same title for all view controllers? However, if same title and same left bar item for all view controllers is what you need. Here are two solutions:
1). Make an extension to UIViewController:
extension UIViewController {
func customAppearance() {
let leftButton = UIBarButtonItem(title: "Info", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(openInfo))
self.navigationItem.leftBarButtonItem = leftButton
self.navigationItem.title = "MYTITLE"
}
func openInfo() {
// do what you want
}
}
And then whenever you need a customised navigation bar for a view controller, you call this customAppearance function:
let vc = YourViewController()
vc.customAppearance()
2). Subclass the UIViewController:
class CustomViewController: UIViewController {
override func viewDidLoad() {
let leftButton = UIBarButtonItem(title: "Info", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(openInfo))
self.navigationItem.leftBarButtonItem = leftButton
self.navigationItem.title = "MYTITLE"
}
func openInfo() {
}
}
And your every other view controllers subclass this CustomViewController.
For customzing UINavigationBar's appearance, you can set it like:
UINavigationBar.appearance().barTintColor = UIColor.purpleColor()
Related
I have a very simple setup. A UINavigationController with a root UIViewController that modifies its navigation item with a custom back button item on viewDidLoad.
let backButton = UIBarButtonItem(image: backArrowImage,
style: .plain,
target: nil,
action: nil)
navigationItem.backBarButtonItem = backButton
I'm expecting this to completely replace the system back button with title and the default back arrow icon.
However when I push a new view controller on the stack, the navigation bar draws both the new custom back icon and the system back icon.
This is what I'm seeing:
This is what I would expect it to look like:
You can hide the back button
navigationItem.hidesBackButton = true
and use leftBarButtonItem for custom UIBarButtonItem
UPD
import UIKit
final class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true
let backItem = UIBarButtonItem(image: backArrowImage, style: .plain, target: self, action: #selector(backButtonPressed))
navigationItem.leftBarButtonItem = backItem
}
#objc func backButtonPressed() {
navigationController?.popViewController(animated: true)
}
}
let backBarButtonItem: UIBarButtonItem = .init(
image: UIImage(systemName: "chevron.backward"),
style: .plain,
target: target,
action: action
)
navigationBar.topItem?.backBarButtonItem = backBarButtonItem
navigationBar.backIndicatorImage = UIImage()
navigationBar.backIndicatorTransitionMaskImage = UIImage()
This works for me, to setup custom "<" and hide the default one and still keep the backBarButtonItem behaviours
The solution was to set global UINavigationBar appearance.
Apparently this has to be done at app launch.
UINavigationBar.appearance().backIndicatorImage = backArrowImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = backArrowImage
With this approach we can preserve title animations and general back button behavior that would not be preserved if supplementing the back button with the leftBarButtonItem.
I have spent all day trying to figure this out and I'm beyond frustrated.
I have a navigation stack and on each screen, based on the content of the screen, I need to hide/show some of the right bar button items. But no matter what I do, the items are changing on the previous screen on the stack, not on the current screen.
My view controllers are all pushed onto a navigation controller. And each view controller can instantiate another view controller and push onto the navigation stack.
let vc = UnifiedArticleViewController()
navigationController?.pushViewController(vc, animated: true)
I have tried the following:
navigationController?.navigationBar.topItem?.rightBarButtonItems = [arrayOfBarButtonItems]
and:
navigationItem.rightBarButtonItems = [arrayOfBarButtonItems]
as well as a variety of other suggestions I've gotten from various stack overflow suggestions.
I have been able to change the title of screens using:
navigationController?.navigationBar.topItem?.title = "New Title Here"
and that works perfectly. What am I doing wrong?
Set the leftBarButtonItems or rightBarButtonItems from the controller you're wanting to set the button(s) on that is in the navigation controller's view stack.
class ViewController: UIViewController {
override func viewDidLoad() {
let leftButton = UIBarButtonItem(title: "Left", style: .done, target: self, action: #selector(leftButtonPressed(_:)))
self.navigationItem.leftBarButtonItems = [leftButton]
let rightButton = UIBarButtonItem(title: "Right", style: .done, target: self, action: #selector(rightButtonPressed(_:)))
self.navigationItem.rightBarButtonItems = [rightButton]
}
#objc func leftButtonPressed(_ sender: UIBarButtonItem) {
}
#objc func rightButtonPressed(_ sender: UIBarButtonItem) {
}
}
How do I set a custom image to all back buttons of view controllers pushed in a UINavigationController?
My issues are:
must look like leftBarButtonItem, position-wise (because the backBarButtonItem itself is too glued to the left and I can't seem to change it's horizontal alignment).
has to be on all back actions (instead of manually setting on each view controller).
having a method setCustomBackButton and calling it on each view controller is also not an option, I'm looking for something like UINavigationBar.appearance(), i.e., throughout the app.
Something like this:
But with the back action working without me manually setting the selector on each view controller.
UPDATE: In response to Joe's solution, I'm getting that error:
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "backArrow")
See Here: https://www.raywenderlich.com/108766/uiappearance-tutorial
Below answer based on the following OP answers:
Custom Back Button With Image and How to remove all navigationbar back button title
Try below code in didFinishLaunchingWithOptions method in AppDelegate.
To setting up a custom back button:
let backArrowImage = UIImage(named: "back") // set your back button image here
let renderedImage = backArrowImage?.withRenderingMode(.alwaysOriginal)
UINavigationBar.appearance().backIndicatorImage = renderedImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = renderedImage
To hide a back button title:
let barAppearace = UIBarButtonItem.appearance()
barAppearace.setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -60), for:UIBarMetrics.default)
Output: Updated
Update:
You need to add the following code to your More Information viewController to keep the title position.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
You can create your own subclass of UINavigationController and change the button inside the navigationController(_:willShow:animated:) delegate method as follows:
class MyNavigationController: UINavigationController, UINavigationControllerDelegate, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
interactivePopGestureRecognizer?.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if viewController != self.viewControllers.first { // don't add button to rootViewController
let backButton = UIBarButtonItem(image: UIImage(named: "backArrow"), style: .plain, target: self, action: #selector(popViewController(animated:)) )
viewController.navigationItem.leftBarButtonItem = backButton
}
}
}
Theoretically the above delegate method could live anywhere, but this way its logical and easy to select where you want to have this functionality.
Also don't forget to set the interactivePopGestureRecognizer delegate for not loosing the edge swipe gesture to go back (this somehow breaks when setting a new leftBarButtonItem).
The above method could be further improved by keeping track of which view controllers were already shown and then only replace the leftBarButtonItem on new ones (right now it also replaces it when going back/popping to an already shown view controller).
Try this Swift 4.2
extension YouFirstViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if !(viewController is YouFirstViewController) {
let backButton = UIBarButtonItem(image: UIImage(named: "icnBack"), style: .plain, target: self, action: #selector(popview))
viewController.navigationItem.leftBarButtonItem = backButton
}
}
#objc func popview() {
navigationController?.popViewController(animated: true)
}
}
onYouFirstViewController
class YouFirstViewController: UIViewcontroller {
override func viewDidLoad() {
self.navigationController?.delegate = self
}
}
I am trying to code without using Storyboard or Interface Builder in my project. So, in the need of create a side menu for my app, I want to create a Navigation Bar to give the chance to open the menu by tapping on the left button of my navigation Bar.
This is what I have tried with no success:
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = ViewController()
centerViewController.delegate = self
centerNavigationController = UINavigationController(rootViewController: centerViewController)
let menuButton: UIBarButtonItem = UIBarButtonItem(title: "TMDB", style: .plain, target: centerViewController, action: Selector(("toggleLeftButton")))
centerNavigationController.navigationItem.leftBarButtonItem = menuButton
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMove(toParentViewController: self)
}
My Navigation Bar is not showing any button nor title.
I know that is no longer necessary but still. His lessons are helpfull https://www.youtube.com/watch?v=zS-CCd4xmRY
Solved
The problem was where I was writting the code. It must be instantiated inside the viewController where the NavigationController belongs.
I mean, inside the centerViewController not in his parent as I was doing.
This lines of code:
let menuButton: UIBarButtonItem = UIBarButtonItem(title: "TMDB", style: .plain, target: self, action: Selector(("toggleLeftButton")))
self.navigationItem.leftBarButtonItem = menuButton
Inside the controller ViewController.
EDIT
Reference:
Adding UIBarButtonItem to UINav..Controller
I can't seem to get any buttons to appear in my navigation bar.
Flow: Navigation Controller -> Tab bar Controller -> ViewController (want to put button here)
I've tried adding it programmatically inside of ViewDidLoad:
let navigationBar = navigationController!.navigationBar
navigationBar.tintColor = UIColor.blackColor()
let leftButton = UIBarButtonItem(title: "Left Button", style: UIBarButtonItemStyle.Plain, target: self, action: nil)
let rightButton = UIBarButtonItem(title: "Right Button", style: UIBarButtonItemStyle.Plain, target: self, action: nil)
navigationItem.leftBarButtonItem = leftButton
navigationItem.rightBarButtonItem = rightButton
I've also tried adding it using the storyboard but it doesnt show during runtime. Any idea why I can't get a button to appear in the navigation bar
Try it like this instead:
self.tabBarController?.navigationItem.leftBarButtonItems = [leftButton]
views from storyboard or xib load after viewWillAppear method call. and viewDidLoad method call before viewWillAppear.
so navigation bar is nil during viewDidLoad method so they are not appear.so write you code in viewdidappear method and your buttons will be appeared.