Related
It works with
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default)
self.navigationController?.navigationBar.shadowImage = UIColor.redColor().as1ptImage()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension UIColor {
func as1ptImage() -> UIImage {
UIGraphicsBeginImageContext(CGSizeMake(1, 1))
let ctx = UIGraphicsGetCurrentContext()
self.setFill()
CGContextFillRect(ctx, CGRect(x: 0, y: 0, width: 1, height: 1))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
But when I add a UITableView it doesn't appear on it and when I add a UISearchView it appears but removes the navigation bar.
Anyone knows how to solve this?
You have to adjust the shadowImage property of the navigation bar.
Try this one. I created a category on UIColor as an helper, but you can refactor the way you prefer.
extension UIColor {
func as1ptImage() -> UIImage {
UIGraphicsBeginImageContext(CGSizeMake(1, 1))
let ctx = UIGraphicsGetCurrentContext()
self.setFill()
CGContextFillRect(ctx, CGRect(x: 0, y: 0, width: 1, height: 1))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Option 1: on a single navigation bar
And then in your view controller (change the UIColor to what you like):
// We can use a 1px image with the color we want for the shadow image
self.navigationController?.navigationBar.shadowImage = UIColor.redColor().as1ptImage()
// We need to replace the navigation bar's background image as well
// in order to make the shadowImage appear. We use the same 1px color tecnique
self.navigationController?.navigationBar.setBackgroundImage(UIColor.yellowColor().as1ptImage(), forBarMetrics: .Default)
Option 2: using appearance proxy, on all navigation bars
Instead of setting the background image and shadow image on each navigation bar, it is possible to rely on UIAppearance proxy. You could try to add those lines to your AppDelegate, instead of adding the previous ones in the viewDidLoad.
// We can use a 1px image with the color we want for the shadow image
UINavigationBar.appearance().shadowImage = UIColor.redColor().as1ptImage()
// We need to replace the navigation bar's background image as well
// in order to make the shadowImage appear. We use the same 1px color technique
UINavigationBar.appearance().setBackgroundImage(UIColor.yellowColor().as1ptImage(), forBarMetrics: .Default)
Wonderful contributions from #TheoF, #Alessandro and #Pavel.
Here is what I did for...
Swift 4
extension UIColor {
/// Converts this `UIColor` instance to a 1x1 `UIImage` instance and returns it.
///
/// - Returns: `self` as a 1x1 `UIImage`.
func as1ptImage() -> UIImage {
UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
setFill()
UIGraphicsGetCurrentContext()?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
UIGraphicsEndImageContext()
return image
}
}
Using it in viewDidLoad():
/* In this example, I have a ViewController embedded in a NavigationController in IB. */
// Remove the background color.
navigationController?.navigationBar.setBackgroundImage(UIColor.clear.as1ptImage(), for: .default)
// Set the shadow color.
navigationController?.navigationBar.shadowImage = UIColor.gray.as1ptImage()
Putting #alessandro-orru's answer in one extension
extension UINavigationController {
func setNavigationBarBorderColor(_ color:UIColor) {
self.navigationBar.shadowImage = color.as1ptImage()
}
}
extension UIColor {
/// Converts this `UIColor` instance to a 1x1 `UIImage` instance and returns it.
///
/// - Returns: `self` as a 1x1 `UIImage`.
func as1ptImage() -> UIImage {
UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
setFill()
UIGraphicsGetCurrentContext()?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
UIGraphicsEndImageContext()
return image
}
}
then in your view controller just add:
self.navigationController?.setNavigationBarBorderColor(UIColor.red)
From iOS 13 on, you can use the UINavigationBarAppearance() class with the shadowColor property:
if #available(iOS 13.0, *) {
let style = UINavigationBarAppearance()
style.shadowColor = UIColor.clear // Effectively removes the border
navigationController?.navigationBar.standardAppearance = style
// Optional info for follow-ups:
// The above will override other navigation bar properties so you may have to assign them here, for example:
//style.buttonAppearance.normal.titleTextAttributes = [.font: UIFont(name: "YourFontName", size: 17)!]
//style.backgroundColor = UIColor.orange
//style.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white,
NSAttributedString.Key.font: UIFont(name: "AnotherFontName", size: 20.0)!]
} else {
// Fallback on earlier versions
}
Solution for Swift 4.0 - 5.2
Here is small extension for changing both Height and Color of bottom navbar line
extension UINavigationController
{
func addCustomBottomLine(color:UIColor,height:Double)
{
//Hiding Default Line and Shadow
navigationBar.setValue(true, forKey: "hidesShadow")
//Creating New line
let lineView = UIView(frame: CGRect(x: 0, y: 0, width:0, height: height))
lineView.backgroundColor = color
navigationBar.addSubview(lineView)
lineView.translatesAutoresizingMaskIntoConstraints = false
lineView.widthAnchor.constraint(equalTo: navigationBar.widthAnchor).isActive = true
lineView.heightAnchor.constraint(equalToConstant: CGFloat(height)).isActive = true
lineView.centerXAnchor.constraint(equalTo: navigationBar.centerXAnchor).isActive = true
lineView.topAnchor.constraint(equalTo: navigationBar.bottomAnchor).isActive = true
}
}
And after adding this extension, you can call this method on any UINavigationController (e.g. from ViewController viewDidLoad())
self.navigationController?.addCustomBottomLine(color: UIColor.black, height: 20)
For iOS 13 and later
guard let navigationBar = navigationController?.navigationBar else { return }
navigationBar.isTranslucent = true
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.backgroundImage = UIImage()
appearance.backgroundColor = .clear
navigationBar.standardAppearance = appearance
} else {
navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationBar.shadowImage = UIImage()
}
for Swift 3.0 just change this line:
CGContextFillRect(ctx, CGRect(x: 0, y: 0, width: 1, height: 1))
to this:
ctx?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
There's a much better option available these days:
UINavigationBar.appearance().shadowImage = UIImage()
I'd like the UITabBar to have a top border of width 5.0. The border should be yellow color.
I don't want any left/bottom/right borders.
The Tab Bar border should be flat (no shadows or anything like that).
How can I remove shadow (image) line?
You can hide the top border this way in your FirstViewController.swift:
self.tabBarController!.tabBar.layer.borderWidth = 0.50
self.tabBarController!.tabBar.layer.borderColor = UIColor.clear.cgColor
self.tabBarController?.tabBar.clipsToBounds = true
And result will be:
Before:
After:
Hope it helps.
EDIT:
You can set background image this way:
UITabBar.appearance().backgroundImage = UIImage(named: "yourImageWithTopYellowBorder.png")
If you want to completely remove tab bar, put this in your AppDelegate:
UITabBar.appearance().shadowImage = UIImage()
UITabBar.appearance().backgroundImage = UIImage()
This is how I get it done. I added a subview on top of the UITabBar.
let lineView = UIView(frame: CGRect(x: 0, y: 0, width:tabBarController.tabBar.frame.size.width, height: 1))
lineView.backgroundColor = UIColor.yellow
tabBarController.tabBar.addSubview(lineView)
This is the complete solution, compiled of different SO answers, that worked for me (Swift 3):
// The tabBar top border is done using the `shadowImage` and `backgroundImage` properties.
// We need to override those properties to set the custom top border.
// Setting the `backgroundImage` to an empty image to remove the default border.
tabBar.backgroundImage = UIImage()
// The `shadowImage` property is the one that we will use to set the custom top border.
// We will create the `UIImage` of 1x5 points size filled with the red color and assign it to the `shadowImage` property.
// This image then will get repeated and create the red top border of 5 points width.
// A helper function that creates an image of the given size filled with the given color.
// http://stackoverflow.com/a/39604716/1300959
func getImageWithColor(color: UIColor, size: CGSize) -> UIImage
{
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size.width, height: size.height))
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
// Setting the `shadowImage` property to the `UIImage` 1x5 red.
tabBar.shadowImage = getImageWithColor(color: UIColor.red, size: CGSize(width: 1.0, height: 5.0))
SWIFT 3
I needed border colors (and line colors and weights) to match other elements in my app, so this worked for me in my custom UITabBarController's viewDidLoad:
tabBar.layer.borderWidth = 0.3
tabBar.layer.borderColor = UIColor(red:0.0/255.0, green:0.0/255.0, blue:0.0/255.0, alpha:0.2).cgColor
tabBar.clipsToBounds = true
UIView *borderLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 5.0)];
borderLine.backgroundColor = [UIColor yellowColor];
[self.tabBarController.tabBar addSubview:borderLine];
This is the way to add border to a UITabBar which I follow.
It works cool.
Swift 4.2
self.tabBarController!.tabBar.layer.borderWidth = 0.50
self.tabBarController!.tabBar.layer.borderColor = UIColor.clear.cgColor
self.tabBarController?.tabBar.clipsToBounds = true
Just change border color as you want.
There is a property named shadowImage that was introduced in iOS 6. You can change this to change the top border. For example, you can use a 1x1px image with a single colour to change the top border to that colour:
UITabBar.appearance().shadowImage = UIImage(named: "TabBarShadow")
You can also set it as just UIImage() to completely remove the top border.
UITabBar.appearance().shadowImage = UIImage()
To answer your question of a 5px border, this can be done by using a 1x5px image. There does not appear to be a limit on the size of the image and it will just repeat (so you could have a dotted line for example by having a 4x5px image where the first 2x5px are black and the next 2x5px are transparent). Note that if you use this, it is outside the bounds of the UITabBar so content will go behind the image unless you change the view bounds.
It's a shadow image (property) of tabbar. Try following solutions and see.
** Swift **
//Remove shadow image by assigning nil value.
UITabBar.appearance().shadowImage = nil
// or
// Assing UIImage instance without image reference
UITabBar.appearance().shadowImage = UIImage()
** Objective-C **
//Remove shadow image by assigning nil value.
[[UITabBar appearance] setShadowImage: nil];
// or
// Assing UIImage instance without image reference
[[UITabBar appearance] setShadowImage: [[UIImage alloc] init]];
Here is apple guideline for shadowImage.
#available(iOS 6.0, *)
open var shadowImage: UIImage?
Default is nil. When non-nil, a custom shadow image to show instead of
the default shadow image. For a custom shadow to be shown, a custom
background image must also be set with -setBackgroundImage: (if the
default background image is used, the default shadow image will be
used).
Firstly create an extension of UIImage as follow
extension UIImage {
func createSelectionIndicator(color: UIColor, size: CGSize, lineWidth: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(CGRect(x: 0, y: 0, width: size.width, height: lineWidth))
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
In your view controller add the following code.
let tabBar = self.tabBarController!.tabBar
tabBar.selectionIndicatorImage = UIImage().createSelectionIndicator(color: UIColor.blue, size: CGSize(width: tabBar.frame.width/CGFloat(tabBar.items!.count), height: tabBar.frame.height) , lineWidth: 5.0)
Just set the UITabBar backgroundImage and shadowImage to be clear color:
tabBar.shadowImage = UIImage.init(color: UIColor.clear)
tabBar.backgroundImage = UIImage.init(color: UIColor.clear)
I'm using an image view to display an image in my nav bar. The problem is that I can't set it to the center correctly because of the back button. I checked the related questions and had almost the same problem earlier that I solved, but this time I have no idea.
Earlier I solved this problem with fake bar buttons, so I tried to add a fake bar button to the right (and left) side, but it doesn't helped.
- (void) searchButtonNavBar {
CGRect imageSizeDummy = CGRectMake(0, 0, 25,25);
UIButton *dummy = [[UIButton alloc] initWithFrame:imageSizeDummy];
UIBarButtonItem
*searchBarButtonDummy =[[UIBarButtonItem alloc] initWithCustomView:dummy];
self.navigationItem.rightBarButtonItem = searchBarButtonDummy;
}
- (void)setNavBarLogo {
[self setNeedsStatusBarAppearanceUpdate];
CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:#"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = logo;
[[UIBarButtonItem appearance] setTitlePositionAdjustment:UIOffsetMake(0.0f, 0.0f) forBarMetrics:UIBarMetricsDefault];
}
I think it should be workin fine because in this case the titleView has bar buttons on the same side. Is there any explanation why it worked with bar buttons that was created programmatically but doesn't works with the common back button?
UINavigationBar automatically centers its titleView as long as there is enough room. If the title isn't centered that means that the title view is too wide to be centered, and if you set the backgroundColor if your UIImageView you'll see that's exactly what is happening.
The title view is too wide because that navigation bar will automatically resize the title to hold its content, using -sizeThatFits:. This means that your title view will always be resized to the size of your image.
Two possible fixes:
The image you're using is way too big. Use a properly sized 44x44 pt image with 2x and 3x versions.
Wrap UIImageView inside of a regular UIView to avoid resizing.
Example:
UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test.jpeg"]];
imageView.contentMode = UIViewContentModeScaleAspectFit;
UIView* titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
imageView.frame = titleView.bounds;
[titleView addSubview:imageView];
self.navigationItem.titleView = titleView;
An example in Swift 3 version of Darren's second way:
let imageView = UIImageView(image: UIImage(named: "test"))
imageView.contentMode = UIViewContentMode.scaleAspectFit
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
imageView.frame = titleView.bounds
titleView.addSubview(imageView)
self.navigationItem.titleView = titleView
I suggest you Override the function - (void)setFrame:(CGRect)fram
like this:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame]; //systom function
self.center = CGPointMake(self.superview.center.x, self.center.y); //rewrite function
}
so that the titleView.center always the right location
Don't use titleView.
Just add your image to navigationController.navigationBar
CGRect myImageS = CGRectMake(0, 0, 44, 44);
UIImageView *logo = [[UIImageView alloc] initWithFrame:myImageS];
[logo setImage:[UIImage imageNamed:#"color.png"]];
logo.contentMode = UIViewContentModeScaleAspectFit;
logo.center = CGPointMake(self.navigationController.navigationBar.width / 2.0, self.navigationController.navigationBar.height / 2.0);
logo.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self.navigationController.navigationBar addSubview:logo];
Qun Li's worked perfectly for me. Here's the swift 2.3 code:
override var frame: CGRect {
set(newValue) {
super.frame = newValue
if let superview = self.superview {
self.center = CGPoint(x: superview.center.x, y: self.center.y)
}
}
get {
return super.frame
}
}
If you're using a custom view from a nib, be sure to disable auto-layout on the nib file.
I created a custom UINavigationController that after dropping in, the only thing you have to do is call showNavBarTitle(title:font:) when you want to show and removeNavBarTitle() when you want to hide:
class NavigationController: UINavigationController {
private static var mTitleFont = UIFont(name: <your desired font (String)> , size: <your desired font size -- however, font size will automatically adjust so the text fits in the label>)!
private static var mNavBarLabel: UILabel = {
let x: CGFloat = 60
let y: CGFloat = 7
let label = UILabel(frame: CGRect(x: x, y: y, width: UIScreen.main.bounds.size.width - 2 * x, height: 44 - 2 * y))
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.font = NavigationController.mTitleFont
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
func showNavBarLabel(title: String, font: UIFont = mTitleFont) {
NavigationController.mNavBarLabel.text = title
NavigationController.mNavBarLabel.font = font
navigationBar.addSubview(NavigationController.mNavBarLabel)
}
func removeNavBarLabel() {
NavigationController.mNavBarLabel.removeFromSuperview()
}
}
I find the best place to call showNavBarTitle(title:font:) and removeNavBarTitle() are in the view controller's viewWillAppear() and viewWillDisappear() methods, respectively:
class YourViewController: UIViewController {
func viewWillAppear() {
(navigationController as! NavigationController).showNavBarLabel(title: "Your Title")
}
func viewWillDisappear() {
(navigationController as! NavigationController).removeNavBarLabel()
}
}
1) You can try setting your image as UINavigationBar's background image by calling
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"color.png"] forBarMetrics:UIBarMetricsDefault];
inside the viewDidLoad method.
That way it will be always centered, but if you have back button with long title as left navigation item, it can appear on top of your logo. And you should probably at first create another image with the same size as the navigation bar, then draw your image at its center, and after that set it as the background image.
2) Or instead of setting your image view as titleView, you can try simply adding at as a subview, so it won't have the constraints related to right and left bar button items.
In Swift, this is what worked for me however it´s not the best solution (basically, add it up to navigationBar):
let titleIV = UIImageView(image: UIImage(named:"some"))
titleIV.contentMode = .scaleAspectFit
titleIV.translatesAutoresizingMaskIntoConstraints = false
if let navigationController = self.navigationController{
navigationController.navigationBar.addSubview(titleIV)
titleIV.centerXAnchor.constraint(equalTo:
navigationController.navigationBar.centerXAnchor).isActive = true
titleIV.centerYAnchor.constraint(equalTo: navigationController.navigationBar.centerYAnchor).isActive = true
}
else{
view.addSubview(titleIV)
titleIV.topAnchor.constraint(equalTo: view.topAnchor, constant: UIApplication.shared.statusBarFrame.height).isActive = true
titleIV.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
Extending Darren's answer, a fix for me was to return a sizeThatFits with the UILabel size. It turns out that this is called after layoutSubViews so the label has a size.
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: titleLabel.frame.width + titleInset*2, height: titleLabel.frame.height)
}
Also note that I have + titleInset*2 because Im setting the horizontal constraints like so:
titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: titleInset),
titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -titleInset)
Simple question, how can I remove the tab bar item text and show only the image?
I want the bar items to like in the instagram app:
In the inspector in xcode 6 I remove the title and choose a #2x (50px) and a #3x (75px) image. However the image does not use the free space of the removed text. Any ideas how to achieve the same tab bar item image like in the instagram app?
You should play with imageInsets property of UITabBarItem. Here is sample code:
let tabBarItem = UITabBarItem(title: nil, image: UIImage(named: "more")
tabBarItem.imageInsets = UIEdgeInsets(top: 9, left: 0, bottom: -9, right: 0)
Values inside UIEdgeInsets depend on your image size. Here is the result of that code in my app:
// Remove the titles and adjust the inset to account for missing title
for(UITabBarItem * tabBarItem in self.tabBar.items){
tabBarItem.title = #"";
tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0);
}
Here is how you do it in a storyboard.
Clear the title text, and set the image inset like the screenshot below
Remember the icon size should follow the apple design guideline
This means you should have 25px x 25px for #1x, 50px x 50px for #2x, 75px x 75px for #3x
Using approach with setting each UITabBarItems title property to ""
and update imageInsets won't work properly if in view controller self.title is set. For example if self.viewControllers of UITabBarController are embedded in UINavigationController and you need title to be displayed on navigation bar. In this case set UINavigationItems title directly using self.navigationItem.title, not self.title.
If you're using storyboards this would be you best option. It loops through all of the tab bar items and for each one it sets the title to nothing and makes the image full screen. (You must have added an image in the storyboard)
for tabBarItem in tabBar.items!
{
tabBarItem.title = ""
tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0)
}
Swift version of ddiego answer
Compatible with iOS 11
Call this function in viewDidLoad of every first child of the viewControllers after setting title of the viewController
Best Practice:
Alternativelly as #daspianist suggested in comments
Make a subclass of like this class BaseTabBarController:
UITabBarController, UITabBarControllerDelegate and put this function
in the subclass's viewDidLoad
func removeTabbarItemsText() {
var offset: CGFloat = 6.0
if #available(iOS 11.0, *), traitCollection.horizontalSizeClass == .regular {
offset = 0.0
}
if let items = tabBar.items {
for item in items {
item.title = ""
item.imageInsets = UIEdgeInsets(top: offset, left: 0, bottom: -offset, right: 0)
}
}
}
iOS 11 throws a kink in many of these solutions, so I just fixed my issues on iOS 11 by subclassing UITabBar and overriding layoutSubviews.
class MainTabBar: UITabBar {
override func layoutSubviews() {
super.layoutSubviews()
// iOS 11: puts the titles to the right of image for horizontal size class regular. Only want offset when compact.
// iOS 9 & 10: always puts titles under the image. Always want offset.
var verticalOffset: CGFloat = 6.0
if #available(iOS 11.0, *), traitCollection.horizontalSizeClass == .regular {
verticalOffset = 0.0
}
let imageInset = UIEdgeInsets(
top: verticalOffset,
left: 0.0,
bottom: -verticalOffset,
right: 0.0
)
for tabBarItem in items ?? [] {
tabBarItem.title = ""
tabBarItem.imageInsets = imageInset
}
}
}
I used the following code in my BaseTabBarController's viewDidLoad.
Note that in my example, I have 5 tabs, and selected image will always be base_image + "_selected".
// Get tab bar and set base styles
let tabBar = self.tabBar;
tabBar.backgroundColor = UIColor.whiteColor()
// Without this, images can extend off top of tab bar
tabBar.clipsToBounds = true
// For each tab item..
let tabBarItems = tabBar.items?.count ?? 0
for i in 0 ..< tabBarItems {
let tabBarItem = tabBar.items?[i] as UITabBarItem
// Adjust tab images (Like mstysf says, these values will vary)
tabBarItem.imageInsets = UIEdgeInsetsMake(5, 0, -6, 0);
// Let's find and set the icon's default and selected states
// (use your own image names here)
var imageName = ""
switch (i) {
case 0: imageName = "tab_item_feature_1"
case 1: imageName = "tab_item_feature_2"
case 2: imageName = "tab_item_feature_3"
case 3: imageName = "tab_item_feature_4"
case 4: imageName = "tab_item_feature_5"
default: break
}
tabBarItem.image = UIImage(named:imageName)!.imageWithRenderingMode(.AlwaysOriginal)
tabBarItem.selectedImage = UIImage(named:imageName + "_selected")!.imageWithRenderingMode(.AlwaysOriginal)
}
Swift 4 approach
I was able to do the trick by implementing a function that takes a TabBarItem and does some formatting to it.
Moves the image a little down to make it be more centered and also hides the text of the Tab Bar.
Worked better than just setting its title to an empty string, because when you have a NavigationBar as well, the TabBar regains the title of the viewController when selected
func formatTabBarItem(tabBarItem: UITabBarItem){
tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
tabBarItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor:UIColor.clear], for: .selected)
tabBarItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor:UIColor.clear], for: .normal)
}
Latest syntax
extension UITabBarItem {
func setImageOnly(){
imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
setTitleTextAttributes([NSAttributedString.Key.foregroundColor:UIColor.clear], for: .selected)
setTitleTextAttributes([NSAttributedString.Key.foregroundColor:UIColor.clear], for: .normal)
}
}
And just use it in your tabBar as:
tabBarItem.setImageOnly()
Here is a better, more foolproof way to do this other than the top answer:
[[UITabBarItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]}
forState:UIControlStateNormal];
[[UITabBarItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]}
forState:UIControlStateHighlighted];
Put this in your AppDelegate.didFinishLaunchingWithOptions so that it affects all tab bar buttons throughout the life of your app.
A minimal, safe UITabBarController extension in Swift (based on #korgx9 answer):
extension UITabBarController {
func removeTabbarItemsText() {
tabBar.items?.forEach {
$0.title = ""
$0.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
}
}
}
Based on the answer of ddiego, in Swift 4.2:
extension UITabBarController {
func cleanTitles() {
guard let items = self.tabBar.items else {
return
}
for item in items {
item.title = ""
item.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
}
}
}
And you just need to call self.tabBarController?.cleanTitles() in your view controller.
Custom TabBar - iOS 13, Swift 5, XCode 11
TabBar items without text
TabBar items centered vertically
Rounded TabBar view
TabBar Dynamic position and frames
Storyboard based. It can be achieved easily programmatically too. Only 4 Steps to follow:
Tab Bar Icons must be in 3 sizes, in black color. Usually, I download from fa2png.io - sizes: 25x25, 50x50, 75x75. PDF image files do not work!
In Storyboard for the tab bar item set the icon you want to use through Attributes Inspector. (see screenshot)
Custom TabBarController -> New File -> Type: UITabBarController -> Set on storyboard. (see screenshot)
UITabBarController class
class RoundedTabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Custom tab bar view
customizeTabBarView()
}
private func customizeTabBarView() {
let tabBarHeight = tabBar.frame.size.height
self.tabBar.layer.masksToBounds = true
self.tabBar.isTranslucent = true
self.tabBar.barStyle = .default
self.tabBar.layer.cornerRadius = tabBarHeight/2
self.tabBar.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let viewWidth = self.view.bounds.width
let leadingTrailingSpace = viewWidth * 0.05
tabBar.frame = CGRect(x: leadingTrailingSpace, y: 200, width: viewWidth - (2 * leadingTrailingSpace), height: 49)
}
}
Result
code:
private func removeText() {
if let items = yourTabBarVC?.tabBar.items {
for item in items {
item.title = ""
}
}
}
In my case, same ViewController was used in TabBar and other navigation flow. Inside my ViewController, I have set self.title = "Some Title" which was appearing in TabBar regardless of setting title nil or blank while adding it in tab bar. I have also set imageInsets as follow:
item.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
So inside my ViewController, I have handled navigation title as follow:
if isFromTabBar {
// Title for NavigationBar when ViewController is added in TabBar
// NOTE: Do not set self.title = "Some Title" here as it will set title of tabBarItem
self.navigationItem.title = "Some Title"
} else {
// Title for NavigationBar when ViewController is opened from navigation flow
self.title = "Some Title"
}
Based on all the great answers on this page, I've crafted another solution that also allows you to show the the title again. Instead of removing the content of title, I just change the font color to transparent.
extension UITabBarItem {
func setTitleColorFor(normalState: UIColor, selectedState: UIColor) {
self.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: normalState], for: .normal)
self.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: selectedState], for: .selected)
}
}
extension UITabBarController {
func hideItemsTitle() {
guard let items = self.tabBar.items else {
return
}
for item in items {
item.setTitleColorFor(normalState: UIColor(white: 0, alpha: 0), selectedState: UIColor(white: 0, alpha: 0))
item.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
}
}
func showItemsTitle() {
guard let items = self.tabBar.items else {
return
}
for item in items {
item.setTitleColorFor(normalState: .black, selectedState: .yellow)
item.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
}
}
Easiest way and always works:
class TabBar: UITabBar {
override func layoutSubviews() {
super.layoutSubviews()
subviews.forEach { subview in
if subview is UIControl {
subview.subviews.forEach {
if $0 is UILabel {
$0.isHidden = true
subview.frame.origin.y = $0.frame.height / 2.0
}
}
}
}
}
}
make a subclass of UITabBarController and assign that to your tabBar , then in the viewDidLoad method place this line of code:
tabBar.items?.forEach({ (item) in
item.imageInsets = UIEdgeInsets.init(top: 8, left: 0, bottom: -8, right: 0)
})
If you are looking to center the tabs / change the image insets without using magic numbers, the following has worked for me (in Swift 5.2.2):
In a UITabBarController subclass, you can add add the image insets after setting the view controllers.
override var viewControllers: [UIViewController]? {
didSet {
addImageInsets()
}
}
func addImageInsets() {
let tabBarHeight = tabBar.frame.height
for item in tabBar.items ?? [] where item.image != nil {
let imageHeight = item.image?.size.height ?? 0
let inset = CGFloat(Int((tabBarHeight - imageHeight) / 4))
item.imageInsets = UIEdgeInsets(top: inset,
left: 0,
bottom: -inset,
right: 0)
}
}
Several of the options above list solutions for dealing with hiding the text.
I have an UITabBar with 5 items. I want to change the unselected color of all items. The items aren't declared in the UIViewController classes (i built them and linked the views in the Storyboard).
Is there an code like this : [[UITabBar appearance] set***UN***SelectedImageTintColor:[UIColor whiteColor]]; ?
In iOS 10 and higher, there are 3 possible easy solutions:
A. Instance from code (Swift):
self.tabBar.unselectedItemTintColor = unselectedcolor
B. Instance from IB:
Add a Key Path: unselectedItemTintColor of type: Color
C. Global appearance (Swift):
UITabBar.appearance().unselectedItemTintColor = unselectedcolor
This will not work under iOS 7 as far as I can say. In particular, tintColor of the tab bar will define the color of the selected tab, not of the unselected ones. If you want to change the default in iOS 7, it seems that you have to actually use different icons (in the color you like to have for unselected tabs) and set the color of the text.
This example should tint selected tabs to red and render others in green. Run this code in your TabBarController:
// set color of selected icons and text to red
self.tabBar.tintColor = [UIColor redColor];
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIColor redColor], NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
// set color of unselected text to green
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor greenColor], NSForegroundColorAttributeName, nil]
forState:UIControlStateNormal];
// set selected and unselected icons
UITabBarItem *item0 = [self.tabBar.items objectAtIndex:0];
// this way, the icon gets rendered as it is (thus, it needs to be green in this example)
item0.image = [[UIImage imageNamed:#"unselected-icon.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// this icon is used for selected tab and it will get tinted as defined in self.tabBar.tintColor
item0.selectedImage = [UIImage imageNamed:#"selected-icon.png"];
If you set the icon in the story board only, you can control the color of the selected tab only (tintColor). All other icons and corresponding text will be drawn in gray.
Maybe someone knows an easier way to adopt the colors under iOS 7?
Extending #Sven Tiffe’s answer for iOS 7, you can get your code to automatically tint the unselected UITabBar images added in the storyboard. The following approach will save you having to create two sets of icon images (i.e. selected vs unselected) and having to programatically load them in. Add the category method imageWithColor: (see - How can I change image tintColor in iOS and WatchKit) to your project then put the following in your custom UITabBarController viewDidLoad method:
// set the selected colors
[self.tabBar setTintColor:[UIColor whiteColor]];
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIColor whiteColor], NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
UIColor * unselectedColor = [UIColor colorWithRed:184/255.0f green:224/255.0f blue:242/255.0f alpha:1.0f];
// set color of unselected text
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:unselectedColor, NSForegroundColorAttributeName, nil]
forState:UIControlStateNormal];
// generate a tinted unselected image based on image passed via the storyboard
for(UITabBarItem *item in self.tabBar.items) {
// use the UIImage category code for the imageWithColor: method
item.image = [[item.selectedImage imageWithColor:unselectedColor] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
Create a Category called UIImage+Overlay and on UIImage+Overlay.m (extracted from this answer ) :
#implementation UIImage(Overlay)
- (UIImage *)imageWithColor:(UIColor *)color1
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextClipToMask(context, rect, self.CGImage);
[color1 setFill];
CGContextFillRect(context, rect);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#end
SO says i cannot delete the accepted answer (i tried), but obviously, there are a lot of upvotes for comments that this doesn't work for iOS 7.
See the other answer below with many more upvotes, or the link in #Liam's comment to this answer.
for iOS 6 only
It should be as simple as this:
[[UITabBar appearance] setTintColor:[UIColor grayColor]]; // for unselected items that are gray
[[UITabBar appearance] setSelectedImageTintColor:[UIColor greenColor]]; // for selected items that are green
Swift version in iOS 10 and higher -
UITabBar.appearance().tintColor = UIColor.gray
UITabBar.appearance().unselectedItemTintColor = UIColor.gray
Translating user3719695's answer to Swift, which now uses extensions:
UIImage+Overlay.swift
extension UIImage {
func imageWithColor(color1: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
color1.setFill()
let context = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(context, 0, self.size.height)
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetBlendMode(context, CGBlendMode.Normal)
let rect = CGRectMake(0, 0, self.size.width, self.size.height) as CGRect
CGContextClipToMask(context, rect, self.CGImage)
CGContextFillRect(context, rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
return newImage
}
}
customTabBar.swift
override func viewDidLoad() {
super.viewDidLoad()
for item in self.tabBar.items! {
item.image = item.selectedImage?.imageWithColor(unselectedColor).imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
//In case you wish to change the font color as well
let attributes = [NSForegroundColorAttributeName: unselectedColor]
item.setTitleTextAttributes(attributes, forState: UIControlState.Normal)
}
}
There is a new appearance API in iOS 13. To color tabbar item's icon and text correctly using Xcode 11.0 you can use it like this:
if #available(iOS 13.0, *)
{
let appearance = tabBar.standardAppearance
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.blue]
appearance.stackedLayoutAppearance.normal.iconColor = UIColor.black
appearance.stackedLayoutAppearance.selected.iconColor = UIColor.blue
tabBar.standardAppearance = appearance
}
else
{
tabBar.unselectedItemTintColor = UIColor.black
tabBar.tintColor = UIColor.blue
}
I had to move the code into viewWillAppear because in viewDidLoad the images weren't set yet.
Swift 4 Translation
import Foundation
import UIKit
extension UIImage {
func with(color: UIColor) -> UIImage {
guard let cgImage = self.cgImage else {
return self
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context = UIGraphicsGetCurrentContext()!
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.setBlendMode(.normal)
let imageRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
context.clip(to: imageRect, mask: cgImage)
color.setFill()
context.fill(imageRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext();
return newImage
}
}
class MYTabBarController: UITabBarController {
let unselectedColor = UIColor(red: 108/255.0, green: 110/255.0, blue: 114/255.0, alpha: 1.0)
let selectedColor = UIColor.blue()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Unselected state colors
for item in self.tabBar.items! {
item.image = item.selectedImage!.with(color: unselectedColor).withRenderingMode(.alwaysOriginal)
}
UITabBarItem.appearance().setTitleTextAttributes([.foregroundColor : unselectedColor], for: .normal)
// Selected state colors
tabBar.tintColor = selectedColor
UITabBarItem.appearance().setTitleTextAttributes([.foregroundColor : selectedColor], for: .selected)
}
}
The new answer to do this programmatically as of iOS 10+ is to use the unselectedItemTintColor API. For example, if you have initialized your tab bar controller inside your AppDelegate, it would looks like the following:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
...
let firstViewController = VC1()
let secondViewController = VC2()
let thirdViewController = VC3()
let tabBarCtrl = UITabBarController()
tabBarCtrl.viewControllers = [firstViewController, secondViewController, thirdViewController]
// set the color of the active tab
tabBarCtrl.tabBar.tintColor = UIColor.white
// set the color of the inactive tabs
tabBarCtrl.tabBar.unselectedItemTintColor = UIColor.gray
...
}
Or just without coding. Swift 4, Xcode 10.1.
Add UITabBar on your View Controller using Interface Builder.
Select the added view in the left panel.
Type cmd + alt + 3 or just click Show the Identity Inspector in the right panel.
In section User Defined Runtime Attributes click on plus button to add a new attribute and call it as unselectedItemTintColor (see here).
Without leaving the section from the previous step (see number 4) under Type column choose Color type.
Finally, set the necessary color under Value section.
Compile your project
Over. Congratulations. 👍🏻
Referring to the answer from here: UITabBar tint in iOS 7
You can set the tint color for selected and unselected tab bar buttons like this:
[[UIView appearanceWhenContainedIn:[UITabBar class], nil] setTintColor:[UIColor redColor]];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor greenColor]];
The first line sets the unselected color - red in this example - by setting the UIView's tintColor when it's contained in a tab bar. Note that this only sets the unselected image's tint color - it doesn't change the color of the text below it.
The second line sets the tab bar's selected image tint color to green.
Swift 4 version (Without implicitly unwrapping Optionals) :
UIImage+Overlay.swift
import UIKit
extension UIImage {
func with(color: UIColor) -> UIImage? {
guard let cgImage = self.cgImage else {
return self
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
if let context = UIGraphicsGetCurrentContext() {
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.setBlendMode(.normal)
let imageRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
context.clip(to: imageRect, mask: cgImage)
color.setFill()
context.fill(imageRect)
if let newImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext();
return newImage
}
}
return nil;
}
}
CustomTabBarController.swift
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 10.0, *) {
self.tabBar.unselectedItemTintColor = UIColor.init(white: 1, alpha: 0.5)
} else {
// Fallback on earlier versions
if let items = self.tabBar.items {
let unselectedColor = UIColor.init(white: 1, alpha: 0.5)
let selectedColor = UIColor.white
// Unselected state colors
for item in items {
if let selectedImage = item.selectedImage?.with(color: unselectedColor)?.withRenderingMode(.alwaysOriginal) {
item.image = selectedImage
}
}
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor : unselectedColor], for: .normal)
// Selected state colors
tabBar.tintColor = selectedColor
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor : selectedColor], for: .selected)
}
}
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "overpass-light", size: 12)!, NSAttributedStringKey.foregroundColor: UIColor.white], for: UIControlState.normal)
}
}
#JoeGalid's imageWithColor: solution with Xamarin:
using CoreGraphics;
using UIKit;
namespace Example
{
public static class UIImageExtensions
{
public static UIImage ImageWithColor(this UIImage image, UIColor color)
{
UIGraphics.BeginImageContextWithOptions(image.Size, false, image.CurrentScale);
color.SetFill();
var context = UIGraphics.GetCurrentContext();
context.TranslateCTM(0, image.Size.Height);
context.ScaleCTM(1.0f, -1.0f);
context.SetBlendMode(CoreGraphics.CGBlendMode.Normal);
var rect = new CGRect(0, 0, image.Size.Width, image.Size.Height);
context.ClipToMask(rect, image.CGImage);
context.FillRect(rect);
var newImage = UIGraphics.GetImageFromCurrentImageContext() as UIImage;
UIGraphics.EndImageContext();
return newImage;
}
}
}
Then utilize it when setting up the tab bar items:
var image = UIImage.FromBundle("name");
barItem.Image = image.ImageWithColor(UIColor.Gray).ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
barItem.SelectedImage = image.ImageWithColor(UIColor.Red).ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
Unselected Color of Tabbar using swift
Get Reference of your TabBarViewController
Use the following code.
[You tabbar controller name]?.tabBar.unselectedItemTintColor = [color name here]
Hope it will help.