I'm new into iOS development and I'd like to know how to create navigation bars like this one:
I've been creating this manually for some time, but have recently discovered there was a way to accomplish this by using Navigation Items and Bars.
I've also tried looking up on the internet on how to do this, but haven't found anything.
Sorry if it is a stupid question and something really easy to do.
Thanks
UINavigationController‘s UINavigationBar has the prefersLargeTitles property. Set it to true to enable large title
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Home"
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshBtnAction(_:)))
}
#objc func refreshBtnAction(_ sender: Any) {
}
In Xcode go to Interface builder, this a tool which display content
of a storyboard or a xib files.
Select a navigation bar
In the attributes inspector which is on the left, you will find a property "Prefers Large Titles". Check it.
Related
I got a navigation bar containing some UIBarButtonItem buttons and a UISearchBar hooked up like this
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
title = "Test"
tableView.delegate = self
tableView.dataSource = self
searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
// This leads to the bug
searchController.hidesNavigationBarDuringPresentation = false
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(leftTapped))
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(rightTapped))
}
Scenario: I tap into the search bar and tap cancel afterwards.
Issue 1: The bar buttons are not reacting to touch except when I touch the outer most pixels of the screen (only possible with the simulator and mouse clicks).
Issue 2: The navigation items are overlapping when I push another view controller.
When I use hidesNavigationBarDuringPresentation = true it's working like expected.
The issue appears on notched and non-notched iPhones iOS 13.0 and 13.1 using Xcode 11.0 and 11.1.
Here's the whole test project:
https://github.com/fl034/HidesNavigationBarDuringPresentationTest
I've filed a radar (and you should too), but maybe some of you guys have already a workaround for it?
Update 1: Bug is still there in iOS 13.1.1
Update 2: Bug is fixed in iOS 13.2 beta (thanks #Ben Gomm)
The view debugger reveals what's going on with this bug. The contents of the navigation bar are being copied. Here's what the navigation bar looks like before you show the search:
And here's what it looks like afterwards:
The two replicant views and the extra UILabel are the problem. I don't know what they're doing there and I can't find a way to remove them.
EDIT By the way, I think some of Apple's apps display the same bug. It's easier to see if you have large titles, because then you can see the large title and the extra label at the same time:
I'm now using this workaround as I want most of my users have the navigation bar visible while search is active (for several app-ux-specific reasons).
var isIosVersionWithNavigationBarBug: Bool {
if #available(iOS 13.2, *) {
return false
}
if #available(iOS 13.0, *) {
return true
}
return false
}
In my search controller I use it like this
mySearchController.hidesNavigationBarDuringPresentation = isIosVersionWithNavigationBarBug
So if iOS 13.2 is being released and the user updates to it, the workaround is not being applied anymore.
This appears to be fixed in iOS 13.2 beta, I tested the example project above using Xcode 11.2 beta (11B41).
Not proud of it but I got it working for now with this hack.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let viewsToRemove = self.navigationController?.navigationBar.subviews.flatMap({ (view) in
view.subviews.filter { type(of: $0) == UILabel.self }
})
viewsToRemove?.forEach { $0.removeFromSuperview() }
}
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 want to change the title of navigation bar but it seems somewhat confusing..
I did self.navigationItem.title = "ipsum" or self.navigationController?.navigationBar.topItem?.title = "ipsum" and so on.. but it fails..
It only succeeds changing the title when trying self.title = "ipsum" but it is not what I want because it causes tabBarItem's title to change too.
I tried debugging and found out strange result.
as you see the picture above.. there seems to be more than two navigationBar so when self.navigationItem.title = "ipsum" it actually changes some other or invisible navigationItem's title so that I can see from the log.
But the UI stays "lorem". Tell me if you figure something out! What am I missing?
EDIT 1:
Main.storyboard
Menu.storyboard
I put NavigationController in TabBarController and refactored NavigationController to another storyboard reference.
self.navigationItem.title = "ipsum" is called in the viewDidLoad() function.
Now View controllers are:
ViewController1 attached to item1:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title="View1"
}
ViewController2 attached to item2:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title="View2"
}
Result:
I used self.navigationItem.title="????". It worked for me:)
You should always use just
navigationItem.title = ""
because each controller has it's own navigationItem and if you trying to set title like this - self.navigationController?.navigationItem.title you possible set it to wrong place.
How do I set a title and buttons on the navigation bar of a pushed view controller if I push it onto navigationController with navigationController.pushViewController(controller:animated:completion:), whilst keeping the "Back" button?
Thank you in advance!
For anyone wondering how to do this, just override your pushed UIViewController's navigationItem property directly. My mistake was that I was trying to accomplish this with self.navigationController?.navigationItem
Here is the code, that you should write in order to set the navigation title as well as left button
Code :
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//For Setting Title
self.title = "New Title";
//For setting button in place of back button
let leftItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "leftButtonClicked");
self.navigationItem.leftBarButtonItem = leftItem;
}
Happy coding ...
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.