I'm using a custom font for the navigationItem; When I segue to another view controller the back button on the newly presented view controller is cut off on the left side. I have tried setTitlePositionAdjustment(_ adjustment: UIOffset, for barMetrics: UIBarMetrics) on the first view controller, before doing segue but it didn't displace the button:
And
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Show Contents" {
if let viewController = segue.destination as? ContentsTableViewController {
viewController.navigationItem.backBarButtonItem?.setTitlePositionAdjustment(UIOffsetMake(10, 0), for: .default)
}
}
}
Using this code:
override func viewDidLoad() {
super.viewDidLoad()
let backBarButtonItem = UIBarButtonItem(title: "خانه", style: .plain, target: nil, action: nil)
backBarButtonItem.setTitleTextAttributes([.font : UIFont(name: "B Koodak", size: 32)!], for: .normal)
navigationItem.backBarButtonItem = backBarButtonItem
}
I get the following:
I encountered this issue when was using a custom font and I was hiding the navigation bar in the parent view and showing it in child views (in viewWillAppear(_:)). Figuring that something in this action was causing the button label to get drawn too narrow, I tried calling various UIKit redrawing methods on the navigation bar (setNeedsLayout() & setNeedsDisplay()) in the child view controllers' viewDidLoad() methods, but to no avail. I was able to get the label to draw properly by making the font size smaller, as the OP wrote.
I was eventually able to get it to draw properly at the correct size by manually setting the backBarButtonItem property to a new instance of UIBarButtonItem with "Back" as the title (in my parent view controller). This is possibly why it worked in the accepted answer. This seems to be a bug in UIKit, as I wouldn't think that hiding and showing the nav bar would cause this behavior.
Related
Since left button is a different thing, and like back bar button it doesn't help in navigation to the last page it is coming from, I am not interested in using it. However I want to edit the back button such that it removes default the back indicator image (arrow) and have text value- "Cancel" and it navigates it to the last page it is coming from.
I have tried few things. Some of them reflects in all of the pages in the app unfortunately and not just on the current page. Some don't work at all (needless to say)
self.navigationController?.navigationBar.backIndicatorImage = UIImage()
Your question has two parts:
Changing the text of the back item
Changing the back indicator image
Changing the Title
The back item of a view controller actually belongs to the previous view controller. You can change this in the previous controller's code (ie. the controller you are coming from) with the following code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let backItem = UIBarButtonItem()
backItem.title = "Cancel" // Change to whatever you want
backItem.tintColor = UIColor.red // The color of the text can be changed too if you want
navigationItem.backBarButtonItem = backItem // Will show in the next view controller being pushed
}
Changing the Back Indicator Image
Changing the title as seen above only changes the text ("cancel" in your case), but does not actually change the indicator icon or image. To do this, go into the class for the view controller you are trying to change.
override func viewDidLoad() {
super.viewDidLoad()
// "image" is file you want
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "image")
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "image")
}
Create a base navigation controller, you can edit image, text color and font in this way.
class BaseNavigationController: UINavigationController {
init() {
super.init(nibName: nil, bundle: nil)
self.navigationBar.titleTextAttributes = [
NSAttributedStringKey.foregroundColor: color,
NSAttributedStringKey.font: font
]
self.navigationBar.barTintColor = color
if #available(iOS 11, *) {
self.navigationBar.backIndicatorImage = image
self.navigationBar.backIndicatorTransitionMaskImage = image
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
try this:
override func viewDidLoad() {
super.viewDidLoad()
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancel))
navigationItem.leftBarButtonItem = cancelButton
}
#objc func cancel(){
navigationController?.popViewController(animated: true)
}
you can also initialize the UIBarButtonItem with a custom image, and you can also do this in the storyboard you don't have to necessarily use code.
I have a UIBarButtonItem, "Done" - created in storyboard IB for ViewController A.
ViewController A is the root View Controller of the navigation stack.
If I push a View Controller B onto the navigation stack and then pop it again. the font weight of the Done button changes.
The font colour of the Done button is applied in A.viewWillAppear(..) and looks pretty much like
doneButton.tintColor = [CMPThemes navigationBarItemColour]; // it's a blue
I have stripped all appearance proxy code from the app (because there
are more than one style of navigation bars/buttons/titles appear in
the app) so I'm not looking for a fix that can only be done via the
appearance proxy...
I have checked in the debug view hierarchy that the Done button is
the same instance before and after the transitions
I have tried to re-apply the tint colour after the pop
I don't apply a font weight anywhere in the process
Also, to my eye, the font and font size seem to be unchanged during
the process.
In ViewController A
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
...
self.navigationController?.pushViewController(vcB, animated: true)
...
In ViewController B
viewDidLoad() {
...
let backButton = UIBarButtonItem(image: UIImage(named: "arrowLeft"), style: .plain, target: self, action: #selector(goBack))
backButton.tintColor = CMPThemes.popoverNavigationBarItemColour()
self.navigationItem.leftBarButtonItem = backButton
}
The storyboard looks like: (I've added the A and B to the image to maintain clarity).
If someone recognises the problem and can point me in the right direction for a fix that would be great!
I found the problem. The applied tint colour is not the issue. I think, Initially when it was loading the done button from the storyboard, the doneButton Style was equals .done and later when you are popping to ViewControllerA, somehow the style getting changed to .plan, so I think setting the style below the tint should fix the issue.
Try updating the viewWillAppear method in ViewControllerA with following code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
doneButton.tintColor = [CMPThemes navigationBarItemColour]; // it's a blue
doneButton.style = .done
}
Hope it helps!
I have faced same issue.
I have back button custom Image and when I push view controller and press back it will getting bold (Default image)
So what I did in storyboard is apply single BLANK space to Back Button
Step 1
--> Tap on Navigation item
Step 2
--> Put blank space on back Button
Now you can observe that
Navigation item has one blank item
I have this below code that changes the back button image on next screen.
I have 30 screens in my app and i want back button to be same across all 30 screens. Is it possible that I don't copy paste this code on all 30 screens and just write it once and rather reuse it across 30 screens.
Also, the code attached with back button should work fine on all screens when i reuse it
I am using iOS 8 and Xcode 6.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let backButtonImage: UIImage = UIImage(named: "back")!
var backBarItem: UIBarButtonItem = UIBarButtonItem(image: backButtonImage, style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
segue.destinationViewController.navigationItem.leftBarButtonItem = backBarItem;
}
func method() {
self.navigationController?.popViewControllerAnimated(true)
}
You can change it globally
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "custom-back")
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "custom-back")
Or per navigation controller.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.backIndicatorImage = UIImage(named: "custom-back")
navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "custom-back")
}
Full detail here https://sarunw.com/posts/how-to-change-back-button-image/
To general change the appearance of UI-Elements in iOS look at UIAppearance. This way you can set it once and it will be everywhere in your app.
I would recommend setting it in the AppDelegate application:didFinishLaunchingWithOptions:.
Try this:
let backImg: UIImage = UIImage(named: "back")!
UIBarButtonItem.appearance().setBackButtonBackgroundImage(backImg, forState: .Normal, barMetrics: .Default)
I only have one navigation Controller in my app, so this may or may not be helpful. But, I created a subclass of UINavigationController. Then in this subclass, I override the pushViewController method:
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
let pushingVC = viewControllers[viewControllers.count - 1]
let backItem = UIBarButtonItem()
backItem.title = ""
pushingVC.navigationItem.backBarButtonItem = backItem
super.pushViewController(viewController, animated: animated)
}
This, makes it so every time a viewController is pushed, from my customNavigationController, it uses the custom back button for every view. You have to make sure you change your UINavigationControllers type to your custom Subclass. But, this works for me in Swift 3.0.
You could use appearance()
Swift
let backImage = UIImage(named: "back")
UINavigationBar.appearance().backIndicatorImage = backImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = backImage
UIBarButtonItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor:
UIColor.white], for: .normal)
UIBarButtonItem.appearance().tintColor = UIColor.green
I'm not quite sure what you wanted, so I'll answer most of what I think you could want for your sake and anyone looking at this in the future.
First: You want a back button similar to those on default apple apps. To do so, you need to a) get a reference to the destination scene and it's view controller. I will assume you have done so and set it equal to controller for future reference. b) You need to set the left bar button item. Set it with:
controller.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Insert Title Here!!", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
Place all of this in prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) (you will also find the destination scene with this, likely with segue.destinationViewController
Second: You want to use an image named "Back" for all items. If so, repeat step a) above, and insert this handy bit of code:
UIBarButtonItem(image: "Back", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
I also know you want to not have to copy it, but it's not as if this is that computer intensive anyways and it's probably simplest.
Third: You're smart and don't hate advances in programming so you want to use a storyboard. If so, simply drag in a Navigation Bar from the object menu, add a navigation item, and copy it to all of your scenes. If your scenes are linked to custom classes, then super happy fun time good news, you can simply link the navigation item to your custom class through an IBAction and use that function to do all the fancy coding your heart could ever desire.
Fourth: You really, really don't want to copy anything even if it's not that hard. If so, you can subclass a UIViewController with your own custom class, add the code I mentioned above, and then subclass all your future UIView classes as this. To be honest, I'm not entirely sure this would work and it seems unnecessarily laborious, but it's up to you.
I'm a bit tired right now so sorry for all the things I'm finding funny, but I hope I helped, and please tell me if I missed your point entirely and I can try and think of a solution.
I have a general question concerning the navigation between views.
I have a view controller 1 embeded in a navigation controller. In the nav bar, I have a button to do add data, when pushed, it goes to view controller 2 through segue Show (in Storyboard). In view controller 2, I return to view controller 1 after collecting data when I click the button save in nav bar on the right. I collect data with:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {...}
I would prefer instead of a back button to have a cancel button in the view controller 2 to return to view controller 1.
I feel something is wrong in my design but I dont know what.
In fact I am looking for something very similar to app clock.
Do you have any idea?
Edit: Here is the story board. you will notice the loop beween the two controller. how to do when I click save to not have a back button automatically on the first controller?
You just have to select your UIBarButtonItem, click at show Attributes Inspector, click at Identifier and select "Cancel" from the drop-down menu.
#IBAction func cancelDownload(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
You can customize the text of the back button by adding this code to the view controller that contains it (in viewDidLoad):
let backItem = UIBarButtonItem(title: "Cancel", style: .Bordered, target: nil, action: nil)
navigationItem.backBarButtonItem = backItem
View hierarchy:
UINavigationController
->UITableViewController(1)
->UITableViewController(2)
->UIViewController(3)
In 1 and 2 I have this code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
self.navigationItem.backBarButtonItem?.title = ""
}
It should override the back button title of the next view controller pushed on the current view controller. It works from 1 -> 2. But it does not work for 2 -> 3. In 3 the back button title has a title, the name of the previous UITableViewController.
Any ideas whats wrong? I am using swift, xcode6.1 and iOS8.1
You could init a new back button with no title. Just put this in the viewDidLoad() of each view controller.
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
--
I am going to extend this answer with my experience. So I managed to remove the title of the back button to display just the back arrow. In storyboard you have to select the navigation item that displays the title inside the navigation bar of the previous view controller. There is a property called Back Button. Just enter a space and save. It will remove the back button title.
Update
UIBarButtonItemStyle.Bordered was deprecated in iOS 8.0. Use UIBarButtonItemStyle.Plain instead.
thanks,
if you want using customer back button image, you can use this
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: “backImage”)
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: “ backImage”)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: “”, style: UIBarButtonItemStyle.Plain, target: nil, action: nil)