Create a custom UINavigationcontroller class - ios

I have done the following customisation to my embedded UINavigationcontroller. I've done this in the view controller of the view that will show up as first.
However I'm trying to clean it up so that I don't have this all in my UIView class by creating a separate class that will take care of this. The code I'm currently using
self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: "navigation.background"), for: .default)
let backButton = UIImage(named: "Back.button")?.withRenderingMode(.alwaysOriginal)
let backButtonHigh = UIImage(named: "Back.button.highlighted")?.withRenderingMode(.alwaysOriginal)
self.navigationController?.navigationBar.backIndicatorImage = backButton
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backButtonHigh
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
What I've tried to do is extend the UInavigationcontroller in a new class like this
class RSRNavigationController: UINavigationController{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: "navigation.background"), for: .default)
let backButton = UIImage(named: "Back.button")?.withRenderingMode(.alwaysOriginal)
let backButtonHigh = UIImage(named: "Back.button.highlighted")?.withRenderingMode(.alwaysOriginal)
self.navigationController?.navigationBar.backIndicatorImage = backButton
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backButtonHigh
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
}
this doesn't work. It will run without problems but it won't actually do anything.
I've also tried to do the following
class RSRNavigationController: UINavigationBar{
override func draw(_ rect: CGRect) {
super.draw(rect)
self.setBackgroundImage(UIImage(named: "navigation.background"), for: .default)
let backButton = UIImage(named: "Back.button")?.withRenderingMode(.alwaysOriginal)
let backButtonHigh = UIImage(named: "Back.button.highlighted")?.withRenderingMode(.alwaysOriginal)
self.backIndicatorImage = backButton
self.backIndicatorTransitionMaskImage = backButtonHigh
//self.backItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
However I can't seem to set the title for the back button.
Does anyone know how to fix this?

Your problem lies here, when you reference navigationController INSIDE your custom navigationController class
Instead of doing
self.navigationController?.navigationBar.backIndicatorImage = backButton
do this
self.navigationBar.backIndicatorImage = backButton
Reason?
Because when you extend your custom class by UINavigationController, it becomes a navigationController itself. And by doing self.navigationController you are telling your app to look for a navigationController within which your custom navigationCOntroller resides. Ofcourse such a nav controller does not exist because your custom navController is the main or the top navController of the app. So just remove the self.navigationController part from your custom class, the rest of the code that you have written in your viewDidLoad should work good.
If you still have any questions feel free to ask

You do not change these properties in the navigation controller itself, you change it in the viewController that is embedded inside a navigation controller.
These customization has to be done in a parent ViewController of your view controllers.
This Answer has more info:
https://stackoverflow.com/a/16913435/4004429

if you just want to custom the backButton of the navigationBar,you can add this code in your child view controller :
func setupLeftBarItem() -> Void {
let originalImage = UIImage.init(named: "leftBatItem")?.withRenderingMode(.alwaysOriginal)
let leftBarItem = UIBarButtonItem.init(image: originalImage, style: .done, target: self, action: #selector(leftBarButtonItemDidTouch))
navigationItem.leftBarButtonItem = leftBarItem
}
func leftBarButtonItemDidTouch() -> Void {
_ = navigationController?.popViewController(animated: true)
}

Related

Why does setting a custom backBarButtonItem for a UINavigationItem result in double back buttons?

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.

How to make a UIBarButtonItem perform a function when pressed?

I am trying to make a custom back button using this code:
let back = UIImage(named: "header_backarrow")
let backView = UIImageView(image: back)
let backItem = UIBarButtonItem(customView: backView)
navigationItem.leftBarButtonItem = backItem
I want the navigation item to perform this code:
func dismissManual() {
dismiss(animated: true, completion: nil)
}
I have tried many things like following this Stack Overflow post: Execute action when back bar button of UINavigationController is pressed
I have also tried making it a navigationItem.backBarButtonItem; however, nothing seems to work. Some things show the correct custom image, but do not work as a button; on the other hand, some work as a button, but do not show the correct image.
Does anybody know how I can show the correct image and make the item work as a button? Thanks.
Do it as follows:
override func viewDidLoad() {
super.viewDidLoad()
let back = UIImage(named: "header_backarrow")
let backView = UIImageView(image: back)
backView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissManual))
backView.addGestureRecognizer(tap)
let backItem = UIBarButtonItem(customView: backView)
navigationItem.leftBarButtonItem = backItem
}
#objc func dismissManual() {
print("print----")
// dismiss(animated: true, completion: nil)
}
Add gesture to backView it will work!
It's similiar to this question IOS - Swift - adding target and action to BarButtonItem's customView
Swift 4.1
The issue is that UIImage does not have tap recognition. You will have to add a tap gesture recognizer to your backView.
lazy var singleTap: UITapGestureRecognizer = {
let singleTap = UITapGestureRecognizer(target: self, action: #selector(tapDetected))
singleTap.numberOfTapsRequired = 1
return singleTap
}()
// Actions
#objc func tapDetected() {
dismiss(animated: true, completion: nil)
}
If you show your code, with some screenshots I can give more help if this doesn't solve the issue.
You are creating your UIBarButtonItem incorrectly. You do not need the image view.
Do it as follows:
let back = UIImage(named: "header_backarrow")
let backItem = UIBarButtonItem(image: back, style: .plain, target: self, action: #selector(dismissManual))
navigationItem.leftBarButtonItem = backItem
#objc func dismissManual() {
dismiss(animated: true, completion: nil)
}
Note that the function must be marked with #objc.
Depending on your image and how you want it displayed, you may need to create the image as follows:
let back = UIImage(named: "header_backarrow").withRenderingMode(.alwaysOriginal)
Another option is to create a UIButton with the image and setup to call your dismissManual function. Create the UIBarButtonItem with the button as the custom view.
But it's easier to create a standard UIBarButtonItem when all you have is a simple image or a simple string.
let barButtonItem = UIBarButtonItem(image: UIImage(named: "backImgs"),
style: .plain,
target: self,
action: #selector(menuButtonTapped))
// Adding button to navigation bar (rightBarButtonItem or leftBarButtonItem)
self.navigationItem.rightBarButtonItem = barButtonItem
// Private action
#objc fileprivate func menuButtonTapped() { // body method here }
Check out this, it may help Thanks.

Setting navigation back button (leftBarButtonItem) only once

I'm trying to set in almost each page a custom back button and I'm repeating the same code in each page like this;
let buttonItem = UIBarButtonItem(image: UIImage(named: "arrow_back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
buttonItem.tintColor=UIColor.blackColor()
navigationItem.leftBarButtonItem = backButtonItem()
And I think this is the wrong way for it. So please could you tell what is the best way for this?
I've done this by creating a category on UIViewController. In that file I created a method called addBackButton, where you can put your code in once, then expose the method in your .h file. Then in any of your view controller subclasses you can import your category and call [self addBackButton];
You have various options here.
1) UIViewController extension
extension UIViewController {
func brandedBackButton() {
let buttonItem = UIBarButtonItem(image: UIImage(named: "back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
buttonItem.tintColor=UIColor.blackColor()
navigationItem.leftBarButtonItem = buttonItem
}
}
and then just call in your view controller
override func viewDidLoad() {
super.viewDidLoad()
brandedBackButton()
}
2) Base View Controller Class
You would basically put a class in between your controllers and the UIViewController.
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let buttonItem = UIBarButtonItem(image: UIImage(named: "back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
buttonItem.tintColor=UIColor.blackColor()
navigationItem.leftBarButtonItem = buttonItem
}
}
and then you would inherit from this BaseViewController instead of standard UIViewController.
class YourWhateverViewController: BaseViewController {
//implementation here....
}
Both the category and the base class can also accommodate the popBack custom method so you would end up really with literally 10 characters to get this behaviour anywhere...
I suggest you create a custom UIButton class and reuse it every time you want it.
class backButtonItem: UIBarButtonItem {
convenience init(target: AnyObject?) {
self.init(image: UIImage(named: "arrow_back"), style: .Plain, target: target, action: "popBack")
self.tintColor = UIColor.blackColor()
}}
just type the following script when you use it:
navigationItem.leftBarButtonItem = backButtonItem(target: self)

not making bar button items and title on nav bar (iOS, Swift)

On my main navigationController, there is a right bar button called "Next Page". When the button is pressed, it calls the method called "nextPage()" which initializes the navigation controller and show it.
The code for nextPage() is like following:
func nextPage() {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
let CustomLoginVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("viewControllerID") as! customVC
let navVC = UINavigationController.init(rootViewController: CustomLoginVC)
navVC.view.autoresizingMask = [.FlexibleHeight, .FlexibleWidth, .FlexibleTopMargin, .FlexibleLeftMargin]
navVC.navigationBar.translucent = false
navVC.navigationItem.title = "haha"
navVC.navigationBar.tintColor = UIColor.whiteColor()
navVC.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "AvenirNext-Medium", size: 22)!, NSForegroundColorAttributeName: UIColor.whiteColor() ]
navVC.navigationItem.leftBarButtonItem?.setTitleTextAttributes([NSFontAttributeName: UIFont(name: "AvenirNext", size: 18)!], forState: UIControlState.Normal)
let backButton = UIBarButtonItem.init(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "backButtonPressedInPDF")
let exportButton = UIBarButtonItem.init(image: UIImage(named: "export"), style: UIBarButtonItemStyle.Plain, target: self, action: "saveAsPDF")
navVC.navigationItem.setLeftBarButtonItem(backButton, animated: false)
navVC.navigationItem.setRightBarButtonItem(exportButton, animated: false)
window.rootViewController = navVC
self.presentViewController(navVC, animated: true, completion: nil)
}
As you can see I'm instantiating CustomLoginVC which is a type of customVC from Main storyboard. customVC is a subclass of UIViewController. Then, initialize UINavigationController with CustomLoginVC and assign left and right bar button items. However, title and bar button items are not shown on the nav bar like below image.
I'm not understanding why bar button items and title are not made on the nav bar.
Also in AppDelegate, I set the method like following:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
UINavigationBar.appearance().barTintColor = UIColor(hex: "00BFA5")
application.statusBarStyle = UIStatusBarStyle.LightContent
return true
}
Any comments are appreciated!
You need to set the title and buttons on CustomLoginVC and not on navVC
I think you should write to below three line try it:
navVC.navigationItem.leftBarButtonItem = nil;
navVC.navigationItem.rightBarButtonItem = nil;
navVC.navigationItem.hidesBackButton = true;
after that try to write:
navVC.navigationItem.setLeftBarButtonItem(backButton, animated: false)
navVC.navigationItem.setRightBarButtonItem(exportButton, animated: false)
OR
self.title = "Your Title"
var homeButton : UIBarButtonItem = UIBarButtonItem(title: "LeftButtonTitle", style: UIBarButtonItemStyle.Plain, target: self, action: "")
var logButton : UIBarButtonItem = UIBarButtonItem(title: "RigthButtonTitle", style: UIBarButtonItemStyle.Plain, target: self, action: "")
self.navigationItem.leftBarButtonItem = homeButton
self.navigationItem.rightBarButtonItem = logButton

performSegue with programmatically added Navigation button

I am trying to customize a Navigation bar button item programmatically... I am just having some issues here which I am sure is an easy fix. I want to make it the default Add button for now, but also in the future will want to make the bar button item a custom icon I create. so I guess I would like to know both ways how to programmatically change a navigation bar button to default styles and custom icons...thanks!
override func viewDidAppear(animated: Bool) {
let rightbutton = UIBarButtonItem(title: "+", style: UIBarButtonItemStyle.Plain, target: self, action: "uploadButtonClicked")
navigationItem.rightBarButtonItem = rightbutton
}
Here is how you would do it with the default add button:
let button = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "uploadButtonClicked")
self.navigationItem.rightBarButtonItem = button
Here is how you would programmatically add a custom image:
let customImage = UIImage(named: "customButtonImage")
let customButton = UIBarButtonItem(image: customImage, style: UIBarButtonItemStyle.Plain, target: self, action: "uploadButtonClicked:")
self.navigationItem.rightBarButtonItem = customButton
To perform the segue, you can do this:
#IBAction func uploadButtonClicked(sender: UIBarButtonItem) {
performSegueWithIdentifier("segueIdentifier", sender: self)
}
Or, if you want to go to another storyboard from there, then your IBAction will look like this:
#IBAction func uploadButtonClicked(sender: UIBarButtonItem) {
let storyboard = UIStoryboard(name: "YourStoryboard", bundle: nil)
let nc = storyboard.instantiateInitialViewController() as! UINavigationController
let vc = nc.viewControllers.first as! YourViewController

Resources