I seem to be unable to correctly set up the back button of the navigationController programmatically that shows when a previous view uses
self.navigationController?.pushViewController(newView, animated: true)
I hide all of the views from the previous view in it's viewDidDisappear using a loop and in the new view presented in the viewDidAppear I attempt to set the action of the back button in various ways; however, while I can succeed in manipulating the back button that is automatically shown such as hiding it or changing it's image I am unable to set up it's action.
Any insight would be appreciated as none of the answers I have found seem to work correctly. Also this is done without any use of the storyboard
if let img = UIImage(named: "backButton") {
self.navigationController?.navigationBar.backIndicatorImage = img
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = img
print("IMAGE")
}
topItem.backBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Rewind, target: self,
action:#selector(self.backButtonAction(_:)))
In your case add a custom button on the Navigation.
class YourViewController: UIViewController {
//Navigation Items.
//left bar button item.
private var leftBarButtonItem : UIBarButtonItem!
//left button.
private var navigationLeftButton : UIButton!
//Your other variable/object declaration.
func viewDidLoad() {
super.viewDidLoad()
self.leftBarButtonItem = UIBarButtonItem()
}
func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.setNavigationBackButton()
}
private func setNavigationBackButton() {
if(self.navigationLeftButton == nil) {
self.navigationLeftButton = UIButton(type: UIButtonType.System)
}
//Styling your navigationLeftButton goes here...
self.navigationLeftButton.addTarget(self, action: Selector("backButtonTapped"), forControlEvents: UIControlEvents.TouchUpInside)
self.leftBarButtonItem.customView = self.navigationLeftButton
self.navigationItem.leftBarButtonItem = self.leftBarButtonItem
}
func backButtonTapped(AnyObject:sender) {
// Here add your custom functionalities.
// Note, this will not pop to previous viewcontroller,
}
}
Related
I want customised navigation bar back button title with action.
My Image :
I need back button like this. Here i added text successfully, but not back arrow. Here my code has action for back button.
This is my code
//Custom barButtonItem with custom alert function
self.navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(title: "< Dialer", style: .plain, target: self, action: #selector(back(sender:)))
self.navigationItem.leftBarButtonItem = newBackButton
When i add above code it's getting like this.
func backButtonConfiguration(_ title : String, backImageName : String) {
let backImage : UIImage = UIImage(named: backImageName)!
self.navigationBar.backIndicatorImage = backImage.withRenderingMode(.alwaysTemplate)
self.navigationBar.backIndicatorTransitionMaskImage = backImage
self.navigationBar.backItem?.title = title
}
Update
if we need custom action for back button then we have to add custom BarButtonItem. And if we need bar back button look same as built in back button then we can create custom view and use it it as a bar button item
weak var customView : UIView!
lazy var leftBarButtonView : UIBarButtonItem! = {
let btnBack = UIBarButtonItem(customView: customView)
let gesture = UITapGestureRecognizer(target: self, action: #selector(btnButtonClicked(_:)))
gesture.numberOfTapsRequired = 1
self.customView.addGestureRecognizer(gesture)
self.customView.isUserInteractionEnabled = true
return btnBack
}()
#objc func btnButtonClicked(_ gesture : UITapGestureRecognizer) {
self.navigationController?.popViewController(animated: true)
}
self.navigationItem.leftBarButtonItems = [leftBarButtonView]
I've a three screens for navigate. But in one these, I can't put a UIBarButtoItem. This screen, is for create a register, and I wants create a button to "Save", that simple. When I did, I select the Bar Button Item, the XCode do not leave me drop on the bar. And programatically, also not works.
I tried this: (Not happens)
var btSalvar : UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
btSalvar = UIBarButtonItem(title: "Salver", style: .plain, target: self, action: nil)
self.navigationItem.rightBarButtonItem = btSalvar
// Do any additional setup after loading the view.
}
And in storyboard: (Note: The Button "Item" not keep fixed on the bar)
You have many options to do that, One of them is:
You have to create a super view controller and add navigation button code in it. I have added a back button for a demo:
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
/**
To add the left back button on navigation.
*/
var addLeftBarMenuButtonEnabled: Bool? {
didSet {
if addLeftBarMenuButtonEnabled! {
let leftBarBtn = UIButton()
leftBarBtn.setImage(UIImage(named: "backIcon"), for: .normal)
leftBarBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
leftBarBtn.addTarget(self, action: #selector(actionBackButton), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem.init(customView: leftBarBtn)
} else {
self.navigationItem.setHidesBackButton(true, animated: true)
}
}
}
///This is action method for back button
#objc func actionBackButton() {
self.view.endEditing(true)
self.navigationController?.popViewController(animated: true)
}
}
Now you need to use the back button in your view controller which super view controller is MainViewController:
class ViewController: MainViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.addLeftBarMenuButtonEnabled = true
}
}
You can add navigation button like that and use where you want. If you want to use it in every view controller then you have to add 'self.addLeftBarMenuButtonEnabled = true' in main view controller like that
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.addLeftBarMenuButtonEnabled = true
}
}
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 have a FirstViewController and a SecondViewController. They have different colors for their UINavigationBar. When I show SecondViewController, the color fades in fine. I recorded the animation with the simulator and slow animations.
However, when I go back from SecondViewController to FirstViewController, the color does not animate and everything just changes at once.
This is how I set the code for the UINavigationBar in SecondViewController.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navBar = self.navigationController?.navigationBar {
navBar.barStyle = UIBarStyle.black
navBar.barTintColor = NavBarColor.red
navBar.backgroundColor = NavBarColor.red
navBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
navBar.isTranslucent = false
navBar.tintColor = .white
}
}
In my FirstViewController class, I created a struct NavBarSettings and save the information of the UINavigationBar. I then apply them in viewWillAppear.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let navBar = self.navigationController?.navigationBar,
let navBarSettings = self.navBarSettings {
navBar.barStyle = navBarSettings.barStyle
navBar.barTintColor = navBarSettings.barTintColor
navBar.backgroundColor = navBarSettings.backgroundColor
navBar.titleTextAttributes = navBarSettings.titleTextAttributes
navBar.isTranslucent = navBarSettings.isTranslucent
navBar.tintColor = navBarSettings.tintColor
}
}
I also tried to change the UINavigationBar information in SecondViewController viewWillDisappear but it had the same effect.
I've also tried to set a backgroundColor but it had did not change anything either.
How do I get the second animation to work like the first one?
Update
The segue to SecondViewController is of kind show.
I simply call it with self.performSegue(withIdentifier: "SecondViewControllerSegue", sender: nil)
I didn't add any custom code to the back button, it's the default UINavigationController implementation.
Try replacing the back button with a custom back button and add an action to it.
let backButton = UIButton()
backButton.addTarget(self, action: #selector(self.backButtonClicked), for: UIControlEvents.touchUpInside)
navBar.navigationItem.leftBarButtonItem = barButton
func backButtonClicked() {
// try implementing the same thing here but with the self.navigationController?.popViewController(animated: true)
}
I have a NavigationBar at the top of a TableView. It looks nice opening/closing the search.
However, if I click on a button in a cell and get directed to another page (with segue); and then use Back button to unwind, it seems like bugged.
()
So it looks like it is pressed and opened but it shouldn't have. It should be looked like the top picture instead (just UIBarButtonItem - search button)
I couldn't figure out the issue creating this problem.
Please note that < Back is created automatically and I didn't write any code to create it. Is there something I am doing wrong?
Update: Added some snippets...
First, created a different class for handling the search
class SearchBarViewController: UIViewController, UISearchBarDelegate {
var searchBar : UISearchBar?
var searchBarWrapper : UIView?
var searchBarButtonItem : UIBarButtonItem?
func constructSearchBar()
{
searchBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: "showSearchBar")
self.navigationItem.rightBarButtonItem = searchBarButtonItem
}
func showSearchBar() {
// styling & configuration
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
UIView.animateWithDuration(0.2, animations: {
self.searchBar?.resignFirstResponder()
self.searchBarWrapper?.alpha = 0
}, completion: { (success) -> Void in
self.searchBar = nil
self.searchBarWrapper = nil
self.navigationItem.rightBarButtonItem = self.searchBarButtonItem
})
}
}
And my ViewController:
class ViewController: SearchBarViewController {
override func viewDidLoad() {
super.viewDidLoad()
constructSearchBar()
}
}
Regarding to emrys57's answer, I tried adding viewWillAppear() in my ViewController but I couldn't make it work, as my cancel looks a little different:
override func viewWillAppear(animated: Bool) {
super.viewDidAppear(animated)
// Here, I couldn't figure out what to put because
// my searchBarCancelButtonClicked() needs searchBar and
// forces me to use (!) but then it says, it's optional..
}
The answer is...
override func viewWillAppear(animated: Bool) {
super.viewDidAppear(animated)
navigationItem.titleView = nil
constructSearchBar()
}
You have not posted code, so it's not entirely clear what's gone wrong. Using UISearchBar, I think you must be handling the buttons separately yourself, as opposed to using UISearchController. I think that you may not be clearing away the search bar when coming back from the second VC. This code clears out the search bar in viewWillAppear:
class ViewController: UIViewController {
var cancelButton: UIBarButtonItem?
var searchButton: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
cancelButton = UIBarButtonItem(barButtonSystemItem: .Cancel, target: self, action: Selector("searchCancelPressed:"))
searchButton = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: Selector("searchPressed:"))
}
override func viewWillAppear(animated: Bool) {
super.viewDidAppear(animated)
searchCancelPressed(nil)
}
func searchPressed(sender: AnyObject) {
navigationItem.titleView = UISearchBar()
navigationItem.rightBarButtonItem = cancelButton
}
func searchCancelPressed(sender: AnyObject?) {
navigationItem.titleView = nil
navigationItem.rightBarButtonItem = searchButton
}
}
and that is working nicely for me when I do a push from a button to the second VC and then hit back.
Following the edit to the original question, this code seems to work, although it may not be the most elegant way of constructing the answer:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationItem.titleView = nil
searchBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: "showSearchBar")
navigationItem.rightBarButtonItem = searchBarButtonItem
}
The function constructSearchBar no longer needs to be called in viewDidLoad, and can be deleted.