Swift 4 Set Navigation Attributes for only one View Controller - ios

I'm trying to make only one view controller's navigation bar translucent and change some of the other attributes.
After the user leaves this VC however I'd like it to go back to 'normal' ie what I have in the AppDelegate.
Do I have to reset each line in the viewWillDisappear? If so what would I use for the background image/shadow image as the default nav bar settings?
// Make Nav Bar Translucent and Set title font/color
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.view.backgroundColor = .clear
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 20, weight: .semibold)]

Yes you have to set the values in ViewWillApear and reset the values in viewWillDisappear function for having clear transparent navigation bar and normal color navigation bar.
You can use extension on UINavigationController which can set/reset values based on some enum value. Create extension on UIImage which can create image from color to apply as a background image on navigation bar and shadow image.
enum NavigationBarMode {
case normal
case clear
}
extension UINavigationController {
func themeNavigationBar(mode: NavigationBarMode) {
self.navigationBar.isTranslucent = true
switch mode {
case .normal:
let image = UIImage.fromColor(.white)
navigationBar.setBackgroundImage(image, for: .default)
navigationBar.shadowImage = UIImage.fromColor(.lightGray)
navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 20, weight: .semibold)]
case .clear:
let image = UIImage()
navigationBar.setBackgroundImage(image, for: .default)
navigationBar.shadowImage = image
navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 20, weight: .semibold)]
}
}
}
extension UIImage {
static func fromColor(_ color: UIColor) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
let context = UIGraphicsGetCurrentContext()
context!.setFillColor(color.cgColor)
context!.fill(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
}
now you can call themeNavigationBar(mode: .transparent) in viewWillApear and call themeNavigationBar(mode: .normal) in viewWillDisappear to reset. also you can call themeNavigationbar(mode: .normal) if you have common navigationbar settings from you other viewControllers.
There are some Third party cocoapods available which you can use
https://github.com/MoZhouqi/KMNavigationBarTransition
https://github.com/DanisFabric/RainbowNavigation

Related

How to change the colors of a segment in a UISegmentedControl in iOS 13?

A UISegmentedControl has a new appearance in iOS 13 and existing code to alter the colors of the segmented control no longer work as they did.
Prior to iOS 13 you could set the tintColor and that would be used for the border around the segmented control, the lines between the segments, and the background color of the selected segment. Then you could change the color of the titles of each segment using the foreground color attribute with titleTextAttributes.
Under iOS 13, the tintColor does nothing. You can set the segmented control's backgroundColor to change the overall color of the segmented control. But I can't find any way to alter the color used as the background of the selected segment. Setting the text attributes still works. I even tried setting the background color of the title but that only affects the background of the title, not the rest of the selected segment's background color.
In short, how do you modify the background color of the currently selected segment of a UISegmentedControl in iOS 13? Is there a proper solution, using public APIs, that doesn't require digging into the private subview structure?
There are no new properties in iOS 13 for UISegmentedControl or UIControl and none of the changes in UIView are relevant.
As of iOS 13b3, there is now a selectedSegmentTintColor on UISegmentedControl.
To change the overall color of the segmented control use its backgroundColor.
To change the color of the selected segment use selectedSegmentTintColor.
To change the color/font of the unselected segment titles, use setTitleTextAttributes with a state of .normal/UIControlStateNormal.
To change the color/font of the selected segment titles, use setTitleTextAttributes with a state of .selected/UIControlStateSelected.
If you create a segmented control with images, if the images are created as template images, then the segmented control's tintColor will be used to color the images. But this has a problem. If you set the tintColor to the same color as selectedSegmentTintColor then the image won't be visible in the selected segment. If you set the tintColor to the same color as backgroundColor, then the images on the unselected segments won't be visible. This means your segmented control with images must use 3 different colors for everything to be visible. Or you can use non-template images and not set the tintColor.
Under iOS 12 or earlier, simply set the segmented control's tintColor or rely on the app's overall tint color.
IOS 13 and Swift 5.0 (Xcode 11.0)Segment Control 100% Working
if #available(iOS 13.0, *) {
yoursegmentedControl.backgroundColor = UIColor.black
yoursegmentedControl.layer.borderColor = UIColor.white.cgColor
yoursegmentedControl.selectedSegmentTintColor = UIColor.white
yoursegmentedControl.layer.borderWidth = 1
let titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
yoursegmentedControl.setTitleTextAttributes(titleTextAttributes, for:.normal)
let titleTextAttributes1 = [NSAttributedString.Key.foregroundColor: UIColor.black]
yoursegmentedControl.setTitleTextAttributes(titleTextAttributes1, for:.selected)
} else {
// Fallback on earlier versions
}
As of Xcode 11 beta 3
There is now the selectedSegmentTintColor property on UISegmentedControl.
See rmaddy's answer
To get back iOS 12 appearance
I wasn't able to tint the color of the selected segment, hopefully it will be fixed in an upcoming beta.
Setting the background image of the selected state doesn't work without setting the background image of the normal state (which removes all the iOS 13 styling)
But I was able to get it back to the iOS 12 appearance (or near enough, I wasn't able to return the corner radius to its smaller size).
It's not ideal, but a bright white segmented control looks a bit out of place in our app.
(Didn't realise UIImage(color:) was an extension method in our codebase. But the code to implement it is around the web)
extension UISegmentedControl {
/// Tint color doesn't have any effect on iOS 13.
func ensureiOS12Style() {
if #available(iOS 13, *) {
let tintColorImage = UIImage(color: tintColor)
// Must set the background image for normal to something (even clear) else the rest won't work
setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
setTitleTextAttributes([.foregroundColor: tintColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
layer.borderWidth = 1
layer.borderColor = tintColor.cgColor
}
}
}
Swift version of #Ilahi Charfeddine answer:
if #available(iOS 13.0, *) {
segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
segmentedControl.selectedSegmentTintColor = UIColor.blue
} else {
segmentedControl.tintColor = UIColor.blue
}
I've tried the workaround and it works great for me. Here's the Objective-C version:
#interface UISegmentedControl (Common)
- (void)ensureiOS12Style;
#end
#implementation UISegmentedControl (Common)
- (void)ensureiOS12Style {
// UISegmentedControl has changed in iOS 13 and setting the tint
// color now has no effect.
if (#available(iOS 13, *)) {
UIColor *tintColor = [self tintColor];
UIImage *tintColorImage = [self imageWithColor:tintColor];
// Must set the background image for normal to something (even clear) else the rest won't work
[self setBackgroundImage:[self imageWithColor:self.backgroundColor ? self.backgroundColor : [UIColor clearColor]] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:tintColorImage forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:[self imageWithColor:[tintColor colorWithAlphaComponent:0.2]] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[self setBackgroundImage:tintColorImage forState:UIControlStateSelected|UIControlStateSelected barMetrics:UIBarMetricsDefault];
[self setTitleTextAttributes:#{NSForegroundColorAttributeName: tintColor, NSFontAttributeName: [UIFont systemFontOfSize:13]} forState:UIControlStateNormal];
[self setDividerImage:tintColorImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
self.layer.borderWidth = 1;
self.layer.borderColor = [tintColor CGColor];
}
}
- (UIImage *)imageWithColor: (UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return theImage;
}
#end
As of Xcode 11 beta 3
There is now the selectedSegmentTintColor property on UISegmentedControl.
Thank you #rmaddy!
Original answer, for Xcode 11 beta and beta 2
Is there a proper solution, using public APIs, that doesn't require digging into the private subview structure?
With Xcode 11.0 beta, it seems to be a challenge to do it by-the-rules, because it basically requires to redraw all the background images for every states by yourself, with round corners, transparency and resizableImage(withCapInsets:). For instance, you would need to generate a colored image similar to:
So for now, the let's-dig-into-the-subviews way seems much easier:
class TintedSegmentedControl: UISegmentedControl {
override func layoutSubviews() {
super.layoutSubviews()
if #available(iOS 13.0, *) {
for subview in subviews {
if let selectedImageView = subview.subviews.last(where: { $0 is UIImageView }) as? UIImageView,
let image = selectedImageView.image {
selectedImageView.image = image.withRenderingMode(.alwaysTemplate)
break
}
}
}
}
}
This solution will correctly apply the tint color to the selection, as in:
iOS13 UISegmentController
how to use:
segment.setOldLayout(tintColor: .green)
extension UISegmentedControl
{
func setOldLayout(tintColor: UIColor)
{
if #available(iOS 13, *)
{
let bg = UIImage(color: .clear, size: CGSize(width: 1, height: 32))
let devider = UIImage(color: tintColor, size: CGSize(width: 1, height: 32))
//set background images
self.setBackgroundImage(bg, for: .normal, barMetrics: .default)
self.setBackgroundImage(devider, for: .selected, barMetrics: .default)
//set divider color
self.setDividerImage(devider, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
//set border
self.layer.borderWidth = 1
self.layer.borderColor = tintColor.cgColor
//set label color
self.setTitleTextAttributes([.foregroundColor: tintColor], for: .normal)
self.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
}
else
{
self.tintColor = tintColor
}
}
}
extension UIImage {
convenience init(color: UIColor, size: CGSize) {
UIGraphicsBeginImageContextWithOptions(size, false, 1)
color.set()
let ctx = UIGraphicsGetCurrentContext()!
ctx.fill(CGRect(origin: .zero, size: size))
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
self.init(data: image.pngData()!)!
}
}
if (#available(iOS 13.0, *)) {
[self.segmentedControl setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont systemFontOfSize:13]} forState:UIControlStateSelected];
[self.segmentedControl setSelectedSegmentTintColor:[UIColor blueColor]];
} else {
[self.segmentedControl setTintColor:[UIColor blueColor]];}
XCODE 11.1 & iOS 13
Based on #Jigar Darji 's answer but a safer implementation.
We first create a failable convenience initialiser:
extension UIImage {
convenience init?(color: UIColor, size: CGSize) {
UIGraphicsBeginImageContextWithOptions(size, false, 1)
color.set()
guard let ctx = UIGraphicsGetCurrentContext() else { return nil }
ctx.fill(CGRect(origin: .zero, size: size))
guard
let image = UIGraphicsGetImageFromCurrentImageContext(),
let imagePNGData = image.pngData()
else { return nil }
UIGraphicsEndImageContext()
self.init(data: imagePNGData)
}
}
Then we extend UISegmentedControl:
extension UISegmentedControl {
func fallBackToPreIOS13Layout(using tintColor: UIColor) {
if #available(iOS 13, *) {
let backGroundImage = UIImage(color: .clear, size: CGSize(width: 1, height: 32))
let dividerImage = UIImage(color: tintColor, size: CGSize(width: 1, height: 32))
setBackgroundImage(backGroundImage, for: .normal, barMetrics: .default)
setBackgroundImage(dividerImage, for: .selected, barMetrics: .default)
setDividerImage(dividerImage,
forLeftSegmentState: .normal,
rightSegmentState: .normal, barMetrics: .default)
layer.borderWidth = 1
layer.borderColor = tintColor.cgColor
setTitleTextAttributes([.foregroundColor: tintColor], for: .normal)
setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
} else {
self.tintColor = tintColor
}
}
}
Here is my take on Jonathan.'s answer for Xamarin.iOS (C#), but with fixes for image sizing. As with Cœur's comment on Colin Blake's answer, I made all images except the divider the size of the segmented control. The divider is 1xheight of the segment.
public static UIImage ImageWithColor(UIColor color, CGSize size)
{
var rect = new CGRect(0, 0, size.Width, size.Height);
UIGraphics.BeginImageContext(rect.Size);
var context = UIGraphics.GetCurrentContext();
context.SetFillColor(color.CGColor);
context.FillRect(rect);
var image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}
// https://stackoverflow.com/a/56465501/420175
public static void ColorSegmentiOS13(UISegmentedControl uis, UIColor tintColor, UIColor textSelectedColor, UIColor textDeselectedColor)
{
if (!UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
return;
}
UIImage image(UIColor color)
{
return ImageWithColor(color, uis.Frame.Size);
}
UIImage imageDivider(UIColor color)
{
return ImageWithColor(color, 1, uis.Frame.Height);
}
// Must set the background image for normal to something (even clear) else the rest won't work
//setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
uis.SetBackgroundImage(image(UIColor.Clear), UIControlState.Normal, UIBarMetrics.Default);
// setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
uis.SetBackgroundImage(image(tintColor), UIControlState.Selected, UIBarMetrics.Default);
// setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
uis.SetBackgroundImage(image(tintColor.ColorWithAlpha(0.2f)), UIControlState.Highlighted, UIBarMetrics.Default);
// setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
uis.SetBackgroundImage(image(tintColor), UIControlState.Highlighted | UIControlState.Selected, UIBarMetrics.Default);
// setTitleTextAttributes([.foregroundColor: tintColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
// Change: support distinct color for selected/de-selected; keep original font
uis.SetTitleTextAttributes(new UITextAttributes() { TextColor = textDeselectedColor }, UIControlState.Normal); //Font = UIFont.SystemFontOfSize(13, UIFontWeight.Regular)
uis.SetTitleTextAttributes(new UITextAttributes() { TextColor = textSelectedColor, }, UIControlState.Selected); //Font = UIFont.SystemFontOfSize(13, UIFontWeight.Regular)
// setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
uis.SetDividerImage(imageDivider(tintColor), UIControlState.Normal, UIControlState.Normal, UIBarMetrics.Default);
//layer.borderWidth = 1
uis.Layer.BorderWidth = 1;
//layer.borderColor = tintColor.cgColor
uis.Layer.BorderColor = tintColor.CGColor;
}
You can implement following method
extension UISegmentedControl{
func selectedSegmentTintColor(_ color: UIColor) {
self.setTitleTextAttributes([.foregroundColor: color], for: .selected)
}
func unselectedSegmentTintColor(_ color: UIColor) {
self.setTitleTextAttributes([.foregroundColor: color], for: .normal)
}
}
Usage code
segmentControl.unselectedSegmentTintColor(.white)
segmentControl.selectedSegmentTintColor(.black)
While answers above are great, most of them get the color of text inside selected segment wrong. I've created UISegmentedControl subclass which you can use on iOS 13 and pre-iOS 13 devices and use the tintColor property as you would on pre iOS 13 devices.
class LegacySegmentedControl: UISegmentedControl {
private func stylize() {
if #available(iOS 13.0, *) {
selectedSegmentTintColor = tintColor
let tintColorImage = UIImage(color: tintColor)
setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
setTitleTextAttributes([.foregroundColor: tintColor!, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
layer.borderWidth = 1
layer.borderColor = tintColor.cgColor
// Detect underlying backgroundColor so the text color will be properly matched
if let background = backgroundColor {
self.setTitleTextAttributes([.foregroundColor: background, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .selected)
} else {
func detectBackgroundColor(of view: UIView?) -> UIColor? {
guard let view = view else {
return nil
}
if let color = view.backgroundColor, color != .clear {
return color
}
return detectBackgroundColor(of: view.superview)
}
let textColor = detectBackgroundColor(of: self) ?? .black
self.setTitleTextAttributes([.foregroundColor: textColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .selected)
}
}
}
override func tintColorDidChange() {
super.tintColorDidChange()
stylize()
}
}
fileprivate extension UIImage {
public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}
Using tintColorDidChange method we ensure that the stylize method will be called every time the tintColor property changes on the segment view, or any of the underlying views, which is preferred behaviour on iOS.
Result:
The SwiftUI Picker lacks some basic options. For people trying to customize a Picker with SegmentedPickerStyle() in SwiftUI in iOS 13 or 14, the simplest option is to use UISegmentedControl.appearance() to set the appearance globally. Here is an example function that could be called to set the appearance.
func setUISegmentControlAppearance() {
UISegmentedControl.appearance().selectedSegmentTintColor = .white
UISegmentedControl.appearance().backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.1)
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.black], for: .normal)
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
}
However, globally setting appearance options with UISegmentedControl.appearance() is not great if you want multiple controls with different settings. Another option is to implement UIViewRepresentable for UISegmentedControl. Here is an example that sets the properties asked about in the the original question and sets .apportionsSegmentWidthsByContent = true as a bonus. Hopefully this will save you some time...
struct MyPicker: UIViewRepresentable {
#Binding var selection: Int // The type of selection may vary depending on your use case
var items: [Any]?
class Coordinator: NSObject {
let parent: MyPicker
init(parent: MyPicker) {
self.parent = parent
}
#objc func valueChanged(_ sender: UISegmentedControl) {
self.parent.selection = Int(sender.selectedSegmentIndex)
}
}
func makeCoordinator() -> MyPicker.Coordinator {
Coordinator(parent: self)
}
func makeUIView(context: Context) -> UISegmentedControl {
let picker = UISegmentedControl(items: self.items)
// Any number of other UISegmentedControl settings can go here
picker.selectedSegmentTintColor = .white
picker.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.1)
picker.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .normal)
picker.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
picker.apportionsSegmentWidthsByContent = true
// Make sure the coordinator updates the picker when the value changes
picker.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged(_:)), for: .valueChanged)
return picker
}
func updateUIView(_ uiView: UISegmentedControl, context: Context) {
uiView.selectedSegmentIndex = self.selection
}
}
In iOS 13 and above, when I accessed the segmented control's subviews in viewDidLoad, it has 1 UIImageView. Once I inserted more segments, it increases respectively, so 3 segments means 3 UIImageView as the segmented control's subviews.
Interestingly, by the time it reaches viewDidAppear, the segmented control's subviews became 3 UISegment (each contained a UISegmentLabel as its subview; this is your text) and 4 UIImageView as shown below:
Those 3 UIImageView in viewDidLoad somehow become UISegment, and we don't want to touch them (but you can try to set their image or isHidden just to see how it affects the UI). Let's ignore these private classes.
Those 4 UIImageView are actually 3 "normal" UIImageView (together with 1 UIImageView subview as the vertical separator) and 1 "selected" UIImageView (i.e. this is actually your selectedSegmentTintColor image, notice in the screenshot above there are no subviews under it).
In my case, I needed a white background, so I had to hide the greyish background images (see: https://medium.com/flawless-app-stories/ios-13-uisegmentedcontrol-3-important-changes-d3a94fdd6763). I also wanted to remove/hide the vertical separators between the segments.
Hence the simple solution in viewDidAppear, without needed to set divider image or background image (in my case), is to simply hide those first 3 UIImageView:
// This method might be a bit 'risky' since we are not guaranteed of the internal sequence ordering, but so far it seems ok.
if #available(iOS 13.0, *) {
for i in 0...(segmentedControl.numberOfSegments - 1) {
segmentedControl.subviews[i].isHidden = true
}
}
or
// This does not depend on the ordering sequence like the above, but this is also risky in the sense that if the UISegment becomes UIImageView one day, this will break.
if #available(iOS 13.0, *) {
for subview in segmentedControl.subviews {
if String(describing: subview).contains("UIImageView"),
subview.subviews.count > 0 {
subview.isHidden = true
}
}
}
Pick your poison...
Gray background in iOS 13+ solution
Since iOS 13 there is introduced updated UISegmentedControl. Unfortunately there is no simple way to setup background to white, as framework is adding semitransparent gray background. In the result, background of UISegmentedControl remains gray. There is workaround for it (ugly, but working):
func fixBackgroundColorWorkaround() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
for i in 0 ... (self.numberOfSegments-1) {
let bg = self.subviews[i]
bg.isHidden = true
}
}
}
iOS 12+
I struggled a lot for background color. Setting .clear color as background color always added default grey color. Here is how i fixed
self.yourSegmentControl.backgroundColor = .clear //Any Color of your choice
self.yourSegmentControl.setBackgroundImage(UIImage(), for: .normal, barMetrics: .default) //This does the magic
Additionally for Divider Color
self.yourSegmentControl.setDividerImage(UIImage(), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default) // This will remove the divider image.
If you want to set the background to clear you have to do this:
if #available(iOS 13.0, *) {
let image = UIImage()
let size = CGSize(width: 1, height: segmentedControl.intrinsicContentSize.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
image.draw(in: CGRect(origin: .zero, size: size))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
segmentedControl.setBackgroundImage(scaledImage, for: .normal, barMetrics: .default)
segmentedControl.setDividerImage(scaledImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
}
Result looks like:

How to make navigation bar transparent in iOS 10

I have the following code to make the navigation bar transparent but while still displaying the back button, this works on all versions of iOS but its stopped working with the iOS 10 beta
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navigationBar.shadowImage = UIImage()
navigationBar.isTranslucent = true
Has something changed with iOS 10 in this area?
Note its not possible to use navigationBar.isHidden as this would result in the navigation bar back button and title etc. disappearing also.
I don't know what has changed in iOS 10 to stop the previous code from working, but to fix it I created a transparent image (it only needs to be one pixel in dimension) and used the following code to make the navigation bar transparent (but still showing the back navigation button).
let transparentPixel = UIImage(named: "TransparentPixel")
navigationBar.setBackgroundImage(transparentPixel, for: UIBarMetrics.default)
navigationBar.shadowImage = transparentPixel
navigationBar.backgroundColor = UIColor.clear()
navigationBar.isTranslucent = true
Incidentally, if you want to change the color of the navigation bar, you can use the same principle:
let redPixel = UIImage(named: "RedPixel")
navigationBar.setBackgroundImage(redPixel, for: UIBarMetrics.default)
navigationBar.shadowImage = redPixel
navigationBar.isTranslucent = false
The solution #Essence provided works perfectly!
This is what I am using even to create the 1px transparent image by code:
class MainClass: UIViewController {
let transparentPixel = UIImage.imageWithColor(color: UIColor.clear)
override func viewWillAppear(_ animated: Bool) {
drawCustomNavigationBar()
}
func drawCustomNavigationBar() {
let nav = (self.navigationController?.navigationBar)!
nav.setBackgroundImage(transparentPixel, for: UIBarMetrics.default)
nav.shadowImage = transparentPixel
nav.isTranslucent = true
}
}
extension UIImage {
class func imageWithColor(color: UIColor) -> UIImage {
let rect = CGRect(origin: CGPoint(x: 0, y:0), size: CGSize(width: 1, height: 1))
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
Swift 3.x
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.backgroundColor = .clear
self.navigationController?.navigationBar.isTranslucent = true

Changing tab bar item image and text color iOS

Here is my tab bar:
The following image shows the program being run and the "NEWS" item selected:
It is clear the bar tint color is working fine as I want !
But the tintColor only affects the image and not the text.
Also, when the an item is selected (as seen above, news) the item color goes blue! How do I prevent this from happening? I want it to stay white.
Why is the text changing to a white color when selected but not when it is unselected?
I basically want the item color and text color to be white all the time.
How do I achieve this? Thanks for any help.
Does it require swift code for each individual item?
EDIT:
From UITabBarItem class docs:
By default, the actual unselected and selected images are
automatically created from the alpha values in the source images. To
prevent system coloring, provide images with
UIImageRenderingModeAlwaysOriginal.
The clue is not whether you use UIImageRenderingModeAlwaysOriginal, the important thing is when to use it.
To prevent the grey color for unselected items, you will just need to prevent the system colouring for the unselected image. Here is how to do this:
var firstViewController:UIViewController = UIViewController()
// The following statement is what you need
var customTabBarItem:UITabBarItem = UITabBarItem(title: nil, image: UIImage(named: "YOUR_IMAGE_NAME")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal), selectedImage: UIImage(named: "YOUR_IMAGE_NAME"))
firstViewController.tabBarItem = customTabBarItem
As you can see, I asked iOS to apply the original color (white, yellow, red, whatever) of the image ONLY for the UNSELECTED state, and leave the image as it is for the SELECTED state.
Also, you may need to add a tint color for the tab bar in order to apply a different color for the SELECTED state (instead of the default iOS blue color). As per your screenshot above, you are applying white color for the selected state:
self.tabBar.tintColor = UIColor.whiteColor()
EDIT:
Swift 3
I did it by creating a custom tabbar controller and added this code inside the viewDidLoad method.
if let count = self.tabBar.items?.count {
for i in 0...(count-1) {
let imageNameForSelectedState = arrayOfImageNameForSelectedState[i]
let imageNameForUnselectedState = arrayOfImageNameForUnselectedState[i]
self.tabBar.items?[i].selectedImage = UIImage(named: imageNameForSelectedState)?.withRenderingMode(.alwaysOriginal)
self.tabBar.items?[i].image = UIImage(named: imageNameForUnselectedState)?.withRenderingMode(.alwaysOriginal)
}
}
let selectedColor = UIColor(red: 246.0/255.0, green: 155.0/255.0, blue: 13.0/255.0, alpha: 1.0)
let unselectedColor = UIColor(red: 16.0/255.0, green: 224.0/255.0, blue: 223.0/255.0, alpha: 1.0)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: unselectedColor], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: selectedColor], for: .selected)
It worked for me!
Swift
For Image:
custom.tabBarItem = UITabBarItem(title: "Home", image: UIImage(named: "tab_icon_normal"), selectedImage: UIImage(named: "tab_icon_selected"))
For Text:
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.gray], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: .selected)
Swift 5 and Xcode 13
The solution that worked for me:
Image setup - from the storyboard set Bar Item Image and Selected Image. To remove the tint overlay on the images go to Assets catalog, select the image and change its rendering mode like this:
This will prevent the Tab bar component from setting its default image tint.
Text - here I created a simple UITabBarController subclass and in its viewDidLoad method I customized the default and selected text color like this:
class HomeTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let appearance = UITabBarItem.appearance(whenContainedInInstancesOf: [HomeTabBarController.self])
appearance.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: .black], for: .normal)
appearance.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: .red], for: .selected)
}
}
Just set this class as your Tab bar controller custom class in identity inspector in IB.
Voila! That's it.
iOS 13 Update:
+ iOS 15 / Xcode 13 Update:
Add this to your setup for iOS 13 and iOS 15:
Update 23/09/2022 for strange behaviour on iOS 15 when built on Xcode 13 when the tab bar is missing:
if #available(iOS 13, *) {
let appearance = UITabBarAppearance()
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: .red]
tabBar.standardAppearance = appearance
// Update for iOS 15, Xcode 13
if #available(iOS 15.0, *) {
tabBar.scrollEdgeAppearance = appearance
}
}
Swift 4: In your UITabBarController change it by this code
tabBar.unselectedItemTintColor = .black
Swift 3
This worked for me (referring to set tabBarItems image colors):
UITabBar.appearance().tintColor = ThemeColor.Blue
if let items = tabBarController.tabBar.items {
let tabBarImages = getTabBarImages() // tabBarImages: [UIImage]
for i in 0..<items.count {
let tabBarItem = items[i]
let tabBarImage = tabBarImages[i]
tabBarItem.image = tabBarImage.withRenderingMode(.alwaysOriginal)
tabBarItem.selectedImage = tabBarImage
}
}
I have noticed that if you set image with rendering mode = .alwaysOriginal, the UITabBar.tintColor doesn't have any effect.
I know here are lots of answers but I can't find an easy and valid copy/paste answer for Swift 4.2/ Swift 5.1
tabBarController?.tabBar.tintColor = UIColor.red
tabBarController?.tabBar.unselectedItemTintColor = UIColor.green
Or use UITabBar.appearances() instead of tabBarController?.tabBar like this:
UITabBar.appearances().tintColor = UIColor.red
UITabBar.appearances().unselectedItemTintColor = UIColor.green
Images have to be UIImageRenderingModeAlwaysTemplate
Swift 3
First of all, make sure you have added the BOOLEAN key "View controller-based status bar appearance" to Info.plist, and set the value to "NO".
Appdelegate.swift
Insert code somewhere after "launchOptions:[UIApplicationLaunchOptionsKey: Any]?) -> Bool {"
Change the color of the tab bar itself with RGB color value:
UITabBar.appearance().barTintColor = UIColor(red: 0.145, green: 0.592, blue: 0.804, alpha: 1.00)
OR one of the default UI colors:
UITabBar.appearance().barTintColor = UIColor.white)
Change the text color of the tab items:
The selected item
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.white], for: .selected)
The inactive items
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.black], for: .normal)
To change the color of the image, I believe the easiest approach is to make to separate images, one for each state.
If you don´t make the icons from scratch, alternating black and white versions are relatively easy to make in Photoshop.
Adobe Photoshop (almost any version will do)
Make sure your icon image has transparent background, and the icon itself is solid black (or close).
Open the image file, save it under a different file name (e.g. exampleFilename-Inverted.png)
In the "Adjustments" submenu on the "Image" menu:
Click "Invert"
You now have a negative of your original icon.
In XCode, set one of the images as "Selected Image" under the Tab Bar Properties in your storyboard, and specify the "inactive" version under "Bar Item" image.
Ta-Da 🍺
Try add it on AppDelegate.swift (inside application method):
UITabBar.appearance().tintColor = UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0)
// For WHITE color:
UITabBar.appearance().tintColor = UIColor(red: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0)
Example:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Tab bar icon selected color
UITabBar.appearance().tintColor = UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0)
// For WHITE color: UITabBar.appearance().tintColor = UIColor(red: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0)
return true
}
Example:
My english is so bad! I'm sorry! :-)
In Swift 5 ioS 13.2 things have changed with TabBar styling, below code work 100%, tested out.
Add the below code in your UITabBarController class.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let appearance = UITabBarAppearance()
appearance.backgroundColor = .white
setTabBarItemColors(appearance.stackedLayoutAppearance)
setTabBarItemColors(appearance.inlineLayoutAppearance)
setTabBarItemColors(appearance.compactInlineLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.stackedLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.inlineLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.compactInlineLayoutAppearance)
tabBar.standardAppearance = appearance
}
#available(iOS 13.0, *)
private func setTabBarItemColors(_ itemAppearance: UITabBarItemAppearance) {
itemAppearance.normal.iconColor = .lightGray
itemAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.gray]
itemAppearance.selected.iconColor = .white
itemAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.orange]
}
#available(iOS 13.0, *)
private func setTabBarItemBadgeAppearance(_ itemAppearance: UITabBarItemAppearance) {
//Adjust the badge position as well as set its color
itemAppearance.normal.badgeBackgroundColor = .orange
itemAppearance.normal.badgeTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
itemAppearance.normal.badgePositionAdjustment = UIOffset(horizontal: 1, vertical: -1)
}
Swift 3.0
I created the tabbar class file and wrote the following code
In viewDidLoad:
self.tabBar.barTintColor = UIColor.white
self.tabBar.isTranslucent = true
let selectedColor = UIColor.red
let unselectedColor = UIColor.cyan
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: unselectedColor,NSFontAttributeName: UIFont(name: "Gotham-Book", size: 10)!], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: selectedColor,NSFontAttributeName: UIFont(name: "Gotham-Book", size: 10)!], for: .selected)
if let items = self.tabBar.items {
for item in items {
if let image = item.image {
item.image = image.withRenderingMode( .alwaysOriginal )
item.selectedImage = UIImage(named: "(Imagename)-a")?.withRenderingMode(.alwaysOriginal)
}
}
}
After viewDidLoad:
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if(item.title! == "title")
{
item.selectedImage = UIImage(named: "(Imagname)-a")?.withRenderingMode(.alwaysOriginal)
}
if(item.title! == "title")
{
item.selectedImage = UIImage(named: "(Imagname)-a")?.withRenderingMode(.alwaysOriginal)
}
if(item.title! == "title")
{
item.selectedImage = UIImage(named: "(Imagname)-a")?.withRenderingMode(.alwaysOriginal)
}
if(item.title! == "title")
{
item.selectedImage = UIImage(named: "(Imagname)-a")?.withRenderingMode(.alwaysOriginal)
}
if(item.title! == "title")
{
item.selectedImage = UIImage(named: "(Imagname)-a")?.withRenderingMode(.alwaysOriginal)
}
}
in view did load method you have to set the selected image and other image are showing with RenderingMode and in tab bar delegate methods you set the selected image as per title
For Swift 4.0, it's now changed as:
tabBarItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.gray], for: .normal)
tabBarItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.blue], for: .selected)
You don't have to subclass the UITabBarItem if your requirement is only to change the text color. Just put the above code inside your view controller's viewDidLoad function.
For global settings change tabBarItem to UITabBarItem.appearance().
In Swift 4.2:
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: .selected)
you can set tintColor of UIBarItem :
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.magentaColor()], forState:.Normal)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.redColor()], forState:.Selected)
You may also do by this way:
override func viewWillLayoutSubviews() {
if let items = self.tabBar.items {
for item in 0..<items.count {
items[item].image = items[item].image?.withRenderingMode(.alwaysOriginal)
items[item].selectedImage = items[item].selectedImage?.withRenderingMode(.alwaysTemplate)
}
Optional:
UITabBar.appearance().tintColor = UIColor.red
I hope it will help you.
Year: 2020 iOS 13.3
Copy below codes to AppDelegate.swift -> func didFinishLaunchingWithOptions
//Set Tab bar text/item fonts and size
let fontAttributes = [NSAttributedString.Key.font: UIFont(name: "YourFontName", size: 12.0)!]
UITabBarItem.appearance().setTitleTextAttributes(fontAttributes, for: .normal)
//Set Tab bar text/item color
UITabBar.appearance().tintColor = UIColor.init(named: "YourColorName")
This Code works for Swift 4 if you want to change the image of Tab Bar Item when pressed.
Copy and paste in the first viewDidLoad method that is hit in the project
let arrayOfImageNameForSelectedState:[String] = ["Image1Color", "Image2Color","Image3Color"]
let arrayOfImageNameForUnselectedState: [String] = ["Image1NoColor","Image2NoColor","Image3NoColor"]
print(self.tabBarController?.tabBar.items?.count)
if let count = self.tabBarController?.tabBar.items?.count {
for i in 0...(count-1) {
let imageNameForSelectedState = arrayOfImageNameForSelectedState[i]
print(imageNameForSelectedState)
let imageNameForUnselectedState = arrayOfImageNameForUnselectedState[i]
print(imageNameForUnselectedState)
self.tabBarController?.tabBar.items?[i].selectedImage = UIImage(named: imageNameForSelectedState)?.withRenderingMode(.alwaysOriginal)
self.tabBarController?.tabBar.items?[i].image = UIImage(named: imageNameForUnselectedState)?.withRenderingMode(.alwaysOriginal)
}
}
If you want to support iOS 13 and above please try this code, because the way to set UItabBar is totally changed from iOS 13.
if #available(iOS 13, *) {
let appearance = UITabBarAppearance()
// appearance.backgroundColor = .white
appearance.shadowImage = UIImage()
appearance.shadowColor = .white
appearance.stackedLayoutAppearance.normal.iconColor = .gray
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.gray]
// appearance.stackedLayoutAppearance.normal.badgeBackgroundColor = .yellow
appearance.stackedLayoutAppearance.selected.iconColor = .systemPink
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.systemPink]
// set padding between tabbar item title and image
appearance.stackedLayoutAppearance.selected.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 4)
appearance.stackedLayoutAppearance.normal.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 4)
self.tabBar.standardAppearance = appearance
} else {
// set padding between tabbar item title and image
UITabBarItem.appearance().titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 4)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.gray], for: .normal)
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.systemPink], for: .selected)
}
From here.
Each tab bar item has a title, selected image, unselected image, and a badge value.
Use the Image Tint (selectedImageTintColor) field to specify the bar item’s tint color when that tab is selected. By default, that color is blue.
Swift 5:
let homeTab = UITabBarItem(title: "Home", image: UIImage(named: "YOUR_IMAGE_NAME_FROM_ASSETS")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal), tag: 1)
Swift 5.3
let vc = UIViewController()
vc.tabBarItem.title = "sample"
vc.tabBarItem.image = UIImage(imageLiteralResourceName: "image.png").withRenderingMode(.alwaysOriginal)
vc.tabBarItem.selectedImage = UIImage(imageLiteralResourceName: "image.png").withRenderingMode(.alwaysOriginal)
// for text displayed below the tabBar item
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .selected)
Since Xcode 13.0 you have options to set this color on the UI too:
Select the tab bar, then in the Inspector customize both Standard and Scroll to Edge Appearance, and below this items you will find a Stacked and Inline customization options. If you select custom, then you will have the "Title Color" setting. You have to set them all (4).
Simply add a new UITabBarController reference to the project.Next create a reference of UITabBar in this controller:
#IBOutlet weak var appTabBar: UITabBar!
In its viewDidLoad(), simply add below for title text color:
appTabBar.tintColor = UIColor.scandidThemeColor()
For image
tabBarItem = UITabBarItem(title: "FirstTab", image: UIImage(named: "firstImage"), selectedImage: UIImage(named: "firstSelectedImage"))
Subclass your TabbarViewController and in ViewDidLoad put this code:
[UITabBarItem.appearance setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor darkGreyColorBT]} forState:UIControlStateNormal];
[UITabBarItem.appearance setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor nightyDarkColorBT]} forState:UIControlStateSelected];
self.tabBar.items[0].image = [[UIImage imageNamed:#"ic-pack off#3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.tabBar.items[0].selectedImage = [[UIImage imageNamed:#"ic-pack#3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.tabBar.items[1].image = [[UIImage imageNamed:#"ic-sleeptracker off#3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.tabBar.items[1].selectedImage = [[UIImage imageNamed:#"ic-sleeptracker#3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.tabBar.items[2].image = [[UIImage imageNamed:#"ic-profile off#3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
self.tabBar.items[2].selectedImage = [[UIImage imageNamed:#"ic-profile#3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
This is the simplest working solution I have
As of Xcode 14/iOS 16, in your UITabBarController subclass, you simply set the tintColor and unselectedItemTintColor properties of tabBar:
tabBar.tintColor = 'some UI color'
tabBar.unselectedItemTintColor = 'some UI color'

Transparent iOS navigation bar

I'm creating an app and i've browsed on the internet and i'm wondering how they make this transparent UINavigationBar like this:
I've added following like in my appdelegate:
UINavigationBar.appearance().translucent = true
but this just makes it look like following:
How can I make the navigation bar transparent like first image?
You can apply Navigation Bar Image like below for Translucent.
Objective-C:
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault]; //UIImageNamed:#"transparent.png"
self.navigationController.navigationBar.shadowImage = [UIImage new];////UIImageNamed:#"transparent.png"
self.navigationController.navigationBar.translucent = YES;
self.navigationController.view.backgroundColor = [UIColor clearColor];
Swift 3:
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) //UIImage.init(named: "transparent.png")
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.view.backgroundColor = .clear
Swift Solution
This is the best way that I've found. You can just paste it into your appDelegate's didFinishLaunchingWithOptions method:
Swift 3 / 4
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Sets background to a blank/empty image
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
// Sets shadow (line below the bar) to a blank image
UINavigationBar.appearance().shadowImage = UIImage()
// Sets the translucent background color
UINavigationBar.appearance().backgroundColor = .clear
// Set translucent. (Default value is already true, so this can be removed if desired.)
UINavigationBar.appearance().isTranslucent = true
return true
}
Swift 2.0
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Sets background to a blank/empty image
UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarMetrics: .Default)
// Sets shadow (line below the bar) to a blank image
UINavigationBar.appearance().shadowImage = UIImage()
// Sets the translucent background color
UINavigationBar.appearance().backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
// Set translucent. (Default value is already true, so this can be removed if desired.)
UINavigationBar.appearance().translucent = true
return true
}
source: Make navigation bar transparent regarding below image in iOS 8.1
Swift 5 applying only to the current view controller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Make the navigation bar background clear
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.isTranslucent = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Restore the navigation bar to default
navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.shadowImage = nil
}
Swift 3 : extension for Transparent Navigation Bar
extension UINavigationBar {
func transparentNavigationBar() {
self.setBackgroundImage(UIImage(), for: .default)
self.shadowImage = UIImage()
self.isTranslucent = true
}
}
Swift 4.2 Solution: For transparent Background:
For General Approach:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
}
For Specific Object:
override func viewDidLoad() {
super.viewDidLoad()
navBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
navBar.shadowImage = UIImage()
navBar.navigationBar.isTranslucent = true
}
Hope it's useful.
I had been working on this, and I was facing a problem using the responses provided here by different users. Problem was a white box behind my NavigationBar transparent image on iOS 13+
My solution is this one
if #available(iOS 13, *) {
navBar?.standardAppearance.backgroundColor = UIColor.clear
navBar?.standardAppearance.backgroundEffect = nil
navBar?.standardAppearance.shadowImage = UIImage()
navBar?.standardAppearance.shadowColor = .clear
navBar?.standardAppearance.backgroundImage = UIImage()
}
Update
thanks to #TMin
If you use a tableView/CollectionView with this you will notice a 1 point shadow appears when you scroll. Add navBar?.scrollEdgeAppearance = nil to get ride of this shadow.
Hope this helps anyone with same problem
I was able to accomplish this in swift this way:
let navBarAppearance = UINavigationBar.appearance()
let colorImage = UIImage.imageFromColor(UIColor.morselPink(), frame: CGRectMake(0, 0, 340, 64))
navBarAppearance.setBackgroundImage(colorImage, forBarMetrics: .Default)
where i created the following utility method in a UIColor category:
imageFromColor(color: UIColor, frame: CGRect) -> UIImage {
UIGraphicsBeginImageContextWithOptions(frame.size, false, 0)
color.setFill()
UIRectFill(frame)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
What it worked for me:
let bar:UINavigationBar! = self.navigationController?.navigationBar
self.title = "Whatever..."
bar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
bar.shadowImage = UIImage()
bar.alpha = 0.0
Set the background property of your navigationBar, e.g.
navigationController?.navigationBar.backgroundColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5)
(You may have to change that a bit if you don't have a navigation controller, but that should give you an idea of what to do.)
Also make sure that the view below actually extends under the bar.
If you want to be able to do this programmatically in swift 4 while staying on the same view,
if change {
navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.backgroundColor = UIColor(displayP3Red: 255/255, green: 206/255, blue: 24/255, alpha: 1)
navigationController?.navigationBar.barTintColor = UIColor(displayP3Red: 255/255, green: 206/255, blue: 24/255, alpha: 1)
} else {
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.setBackgroundImage(backgroundImage, for: .default)
navigationController?.navigationBar.backgroundColor = .clear
navigationController?.navigationBar.barTintColor = .clear
}
One important thing to remember though is to click this button in your storyboard. I had an issue with a jumping display for a long time. Make sureyou set this:
Then when you change the translucency of the navigation bar it will not cause the views to jump as the views extend all the way to the top, regardless of the visiblity of the navigation bar.
Add this in your did load
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.0)
//adjust alpha according to your need 0 is transparent 1 is solid
For those looking for OBJC solution, to be added in App Delegate didFinishLaunchingWithOptions method:
[[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[UINavigationBar appearance].shadowImage = [UIImage new];
[UINavigationBar appearance].backgroundColor = [UIColor clearColor];
[UINavigationBar appearance].translucent = YES;
Try this, it works for me if you also need to support ios7, it is based on the transparency of UItoolBar:
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.translucent = YES;
self.navigationController.view.backgroundColor = [UIColor clearColor];
UIToolbar* blurredView = [[UIToolbar alloc] initWithFrame:self.navigationController.navigationBar.bounds];
[blurredView setBarStyle:UIBarStyleBlack];
[blurredView setBarTintColor:[UIColor redColor]];
[self.navigationController.navigationBar insertSubview:blurredView atIndex:0];
iOS 13.0+ introduced UINavigationBarAppearance because of which, this problem occurs on iOS 13.0+
Use this to solve.
Change Navigation Bar Appearance
Use UINavigationBarAppearance and UIBarButtonItemAppearance to change the appearance of the navigation bar.
// Make the navigation bar's title with red text.
if #available(iOS 13, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UIColor.systemRed
appearance.titleTextAttributes = [.foregroundColor: UIColor.lightText] // With a red background, make the title more readable.
navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
navigationItem.compactAppearance = appearance // For iPhone small navigation bar in landscape.
}
Utility method which you call by passing navigationController and color which you like to set on navigation bar. For transparent you can use clearColor of UIColor class.
For objective c -
+ (void)setNavigationBarColor:(UINavigationController *)navigationController
color:(UIColor*) color {
[navigationController setNavigationBarHidden:false animated:false];
[navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[navigationController.navigationBar setShadowImage:[UIImage new]];
[navigationController.navigationBar setTranslucent:true];
[navigationController.view setBackgroundColor:color];
[navigationController.navigationBar setBackgroundColor:color];
}
For Swift 3.0 -
class func setNavigationBarColor(navigationController : UINavigationController?,
color : UIColor) {
navigationController?.setNavigationBarHidden(false, animated: false)
navigationController?.navigationBar .setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.translucent = true
navigationController?.view.backgroundColor = color
navigationController?.navigationBar.backgroundColor = color
}
Write these two lines:
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.backgroundColor = .clear
Worked for me in iOS 13
None of the answers here fully worked for me. This makes the navigation bar fully transparent - tested on iOS 14 and iOS 11 (Objective C):
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.translucent = YES;
self.navigationController.navigationBar.backgroundColor = [UIColor clearColor];
Anyone looking for a iOS 15+ working version, this is what worked for me, as the old techniques with setBackgroundImage/shadowImage were not working anymore.
To se it transparent:
func setTransparent() {
backgroundColor = .clear
isTranslucent = true
standardAppearance.shadowColor = .clear
standardAppearance.backgroundColor = .clear
standardAppearance.backgroundEffect = nil
scrollEdgeAppearance = standardAppearance
}
To remove transparency:
func removeTransparent() {
setBackgroundImage(nil, for: .default)
shadowImage = nil
backgroundColor = .white
isTranslucent = false
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
standardAppearance = appearance
scrollEdgeAppearance = standardAppearance
}
My implementation of navigation bar configuration as translucent and switching to default state for iOS 15 and older versions:
extension UINavigationBar {
static let defaultBackgroundColor = UIColor.red
static let defaultTintColor = UIColor.white
func setTranslucent(tintColor: UIColor, titleColor: UIColor) {
if #available(iOS 15, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
appearance.titleTextAttributes = [.foregroundColor: titleColor]
standardAppearance = appearance
scrollEdgeAppearance = appearance
} else {
titleTextAttributes = [.foregroundColor: titleColor]
setBackgroundImage(UIImage(), for: UIBarMetrics.default)
shadowImage = UIImage()
}
isTranslucent = true
self.tintColor = tintColor
}
func setDefaultState() {
isTranslucent = false
clipsToBounds = false
if #available(iOS 15, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UINavigationBar.defaultBackgroundColor
appearance.titleTextAttributes = [.foregroundColor: UINavigationBar.defaultTintColor]
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
} else {
setBackgroundImage(UIImage(), for: UIBarPosition.any, barMetrics: UIBarMetrics.defaultPrompt)
shadowImage = UIImage()
barTintColor = UINavigationBar.defaultBackgroundColor
titleTextAttributes = [.foregroundColor: UINavigationBar.defaultTintColor]
}
tintColor = UINavigationBar.defaultTintColor
}
}
This will defiantly work for swift 4/5 users.
func setUpNavBar(){
navigationItem.title = "Flick"
navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.view.backgroundColor = UIColor.clear
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.white]
}
IOS15 Version
extension UIViewController {
func clearNavigationBar(clear: Bool) {
if clear {
let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()
self.navigationController?.navigationBar.standardAppearance = appearance
self.navigationController?.navigationBar.scrollEdgeAppearance = appearance
} else {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
self.navigationController?.navigationBar.standardAppearance = appearance
self.navigationController?.navigationBar.scrollEdgeAppearance = appearance
}
}
}
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
clearNavigationBar(clear: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
clearNavigationBar(clear: false)
}
}
For above all iOS version
if #available(iOS 15.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundImage = UIColor.clear.imageWithColor(width: UIScreen.main.bounds.size.width, height: 84)
appearance.shadowImage = UIImage()
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.black ,NSAttributedString.Key.font : UIFont(name: "SF UI Display Semibold", size: 18) ?? UIFont()]
appearance.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 2)
self.navigationBar.standardAppearance = appearance
} else {
self.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationBar.shadowImage = UIImage()
self.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.black ,NSAttributedString.Key.font : UIFont(name: "SF UI Display Semibold", size: 18) ?? UIFont()]
self.navigationBar.setTitleVerticalPositionAdjustment(2, for: UIBarMetrics.default)
}
func imageWithColor(width: CGFloat, height: CGFloat) -> UIImage {
let size = CGSize(width: width, height: height)
return UIGraphicsImageRenderer(size: size).image { rendererContext in
self.setFill()
rendererContext.fill(CGRect(origin: .zero, size: size))
}
}
Just add bellow code line inside your application delegate
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
// Sets shadow (line below the bar) to a blank image
UINavigationBar.appearance().shadowImage = UIImage()
// Sets the translucent background color
UINavigationBar.appearance().backgroundColor = .clear
// Set translucent. (Default value is already true, so this can be removed if desired.)
UINavigationBar.appearance().isTranslucent = true
then override your custom nav bar inside your view controller and make sure to reset once it disappear

Changing navigation bar color in Swift

I am using a Picker View to allow the user to choose the colour theme for the entire app.
I am planning on changing the colour of the navigation bar, background and possibly the tab bar (if that is possible).
I've been researching how to do this but can't find any Swift examples. Could anyone please give me an example of the code I would need to use to change the navigation bar colour and navigation bar text colour?
The Picker View is set up, I'm just looking for the code to change the UI colours.
Navigation Bar:
navigationController?.navigationBar.barTintColor = UIColor.green
Replace greenColor with whatever UIColor you want, you can use an RGB too if you prefer.
Navigation Bar Text:
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.orange]
Replace orangeColor with whatever color you like.
Tab Bar:
tabBarController?.tabBar.barTintColor = UIColor.brown
Tab Bar Text:
tabBarController?.tabBar.tintColor = UIColor.yellow
On the last two, replace brownColor and yellowColor with the color of your choice.
Here are some very basic appearance customization that you can apply app wide:
UINavigationBar.appearance().backgroundColor = UIColor.greenColor()
UIBarButtonItem.appearance().tintColor = UIColor.magentaColor()
//Since iOS 7.0 UITextAttributeTextColor was replaced by NSForegroundColorAttributeName
UINavigationBar.appearance().titleTextAttributes = [UITextAttributeTextColor: UIColor.blueColor()]
UITabBar.appearance().backgroundColor = UIColor.yellowColor();
Swift 5.4.2:
UINavigationBar.appearance().backgroundColor = .green // backgorund color with gradient
// or
UINavigationBar.appearance().barTintColor = .green // solid color
UIBarButtonItem.appearance().tintColor = .magenta
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.blue]
UITabBar.appearance().barTintColor = .yellow
More about UIAppearance API in Swift you can read here.
Updated for Swift 3, 4, 4.2, 5+
// setup navBar.....
UINavigationBar.appearance().barTintColor = .black
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
Swift 4
UINavigationBar.appearance().barTintColor = .black
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
Swift 4.2, 5+
UINavigationBar.appearance().barTintColor = .black
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
if you want to work with large title, add this line:
UINavigationBar.navigationBar.prefersLargeTitles = true
Also can check here : https://github.com/hasnine/iOSUtilitiesSource
UINavigationBar.appearance().barTintColor = UIColor(red: 46.0/255.0, green: 14.0/255.0, blue: 74.0/255.0, alpha: 1.0)
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.whiteColor()]
Just paste this line in didFinishLaunchingWithOptions in your code.
Within AppDelegate, this has globally changed the format of the NavBar and removes the bottom line/border (which is a problem area for most people) to give you what I think you and others are looking for:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
UINavigationBar.appearance().setBackgroundImage(UIImage(), forBarPosition: UIBarPosition.Any, barMetrics: UIBarMetrics.Default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
UINavigationBar.appearance().barTintColor = Style.SELECTED_COLOR
UINavigationBar.appearance().translucent = false
UINavigationBar.appearance().clipsToBounds = false
UINavigationBar.appearance().backgroundColor = Style.SELECTED_COLOR
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName : (UIFont(name: "FONT NAME", size: 18))!, NSForegroundColorAttributeName: UIColor.whiteColor()] }
Then you can setup a Constants.swift file, and contained is a Style struct with colors and fonts etc. You can then add a tableView/pickerView to any ViewController and use "availableThemes" array to allow user to change themeColor.
The beautiful thing about this is you can use one reference throughout your whole app for each colour and it'll update based on the user's selected "Theme" and without one it defaults to theme1():
import Foundation
import UIKit
struct Style {
static let availableThemes = ["Theme 1","Theme 2","Theme 3"]
static func loadTheme(){
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey("Theme"){
// Select the Theme
if name == availableThemes[0] { theme1() }
if name == availableThemes[1] { theme2() }
if name == availableThemes[2] { theme3() }
}else{
defaults.setObject(availableThemes[0], forKey: "Theme")
theme1()
}
}
// Colors specific to theme - can include multiple colours here for each one
static func theme1(){
static var SELECTED_COLOR = UIColor(red:70/255, green: 38/255, blue: 92/255, alpha: 1) }
static func theme2(){
static var SELECTED_COLOR = UIColor(red:255/255, green: 255/255, blue: 255/255, alpha: 1) }
static func theme3(){
static var SELECTED_COLOR = UIColor(red:90/255, green: 50/255, blue: 120/255, alpha: 1) } ...
To do this on storyboard (Interface Builder Inspector)
With help of IBDesignable, we can add more options to Interface Builder Inspector for UINavigationController and tweak them on storyboard. First, add the following code to your project.
#IBDesignable extension UINavigationController {
#IBInspectable var barTintColor: UIColor? {
set {
guard let uiColor = newValue else { return }
navigationBar.barTintColor = uiColor
}
get {
guard let color = navigationBar.barTintColor else { return nil }
return color
}
}
}
Then simply set the attributes for navigation controller on storyboard.
This approach may also be used to manage the color of the navigation bar text from the storyboard:
#IBInspectable var barTextColor: UIColor? {
set {
guard let uiColor = newValue else {return}
navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: uiColor]
}
get {
guard let textAttributes = navigationBar.titleTextAttributes else { return nil }
return textAttributes[NSAttributedStringKey.foregroundColor] as? UIColor
}
}
Swift 4:
Perfectly working code to change the navigation bar appearance at application level.
// MARK: Navigation Bar Customisation
// To change background colour.
UINavigationBar.appearance().barTintColor = .init(red: 23.0/255, green: 197.0/255, blue: 157.0/255, alpha: 1.0)
// To change colour of tappable items.
UINavigationBar.appearance().tintColor = .white
// To apply textAttributes to title i.e. colour, font etc.
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor : UIColor.white,
.font : UIFont.init(name: "AvenirNext-DemiBold", size: 22.0)!]
// To control navigation bar's translucency.
UINavigationBar.appearance().isTranslucent = false
Happy Coding!
UINavigationBar.appearance().barTintColor
worked for me
SWIFT 4 - Smooth transition (best solution):
If you're moving back from a navigation controller and you have to set a diffrent color on the navigation controller you pushed from you want to use
override func willMove(toParentViewController parent: UIViewController?) {
navigationController?.navigationBar.barTintColor = .white
navigationController?.navigationBar.tintColor = Constants.AppColor
}
instead of putting it in the viewWillAppear so the transition is cleaner.
SWIFT 4.2
override func willMove(toParent parent: UIViewController?) {
navigationController?.navigationBar.barTintColor = UIColor.black
navigationController?.navigationBar.tintColor = UIColor.black
}
Below Codes are working for iOS 15
if #available(iOS 15, *) {
// Navigation Bar background color
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = UIColor.yourColor
// setup title font color
let titleAttribute = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 25, weight: .bold), NSAttributedString.Key.foregroundColor: UIColor.yourColor]
appearance.titleTextAttributes = titleAttribute
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
}
In Swift 4
You can change the color of navigation bar. Just use this below code snippet in viewDidLoad()
Navigation Bar color
self.navigationController?.navigationBar.barTintColor = UIColor.white
Navigation Bar Text Color
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.purple]
For iOS 11 Large Title Navigation Bar, you need to use largeTitleTextAttributes property
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.purple]
The appearance() function not always work for me. So I prefer to create a NC object and change its attributes.
var navBarColor = navigationController!.navigationBar
navBarColor.barTintColor =
UIColor(red: 255/255.0, green: 0/255.0, blue: 0/255.0, alpha: 100.0/100.0)
navBarColor.titleTextAttributes =
[NSForegroundColorAttributeName: UIColor.whiteColor()]
Also if you want to add an image instead of just text, that works as well
var imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 70, height: 70))
imageView.contentMode = .ScaleAspectFit
var image = UIImage(named: "logo")
imageView.image = image
navigationItem.titleView = imageView
Swift 5 (iOS 14)
Full navigation bar customization.
// -----------------------------------------------------------
// NAVIGATION BAR CUSTOMIZATION
// -----------------------------------------------------------
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationBar.tintColor = UIColor.white
self.navigationController?.navigationBar.isTranslucent = false
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithDefaultBackground()
appearance.backgroundColor = UIColor.blue
appearance.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
navigationController?.navigationBar.compactAppearance = appearance
} else {
self.navigationController?.navigationBar.barTintColor = UIColor.blue
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
}
// -----------------------------------------------------------
// NAVIGATION BAR SHADOW
// -----------------------------------------------------------
self.navigationController?.navigationBar.layer.masksToBounds = false
self.navigationController?.navigationBar.layer.shadowColor = UIColor.black.cgColor
self.navigationController?.navigationBar.layer.shadowOffset = CGSize(width: 0, height: 2)
self.navigationController?.navigationBar.layer.shadowRadius = 15
self.navigationController?.navigationBar.layer.shadowOpacity = 0.7
Swift 5, an easy approach with UINavigationController extension. At the bottom of this answer are extensions and previews.
First view controller (Home):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setTintColor(.white)
navigationController?.backgroundColor(.orange)
}
Second view controller (Details):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.transparentNavigationBar()
navigationController?.setTintColor(.black)
}
Extensions for UINavigationController:
extension UINavigationController {
func transparentNavigationBar() {
self.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationBar.shadowImage = UIImage()
self.navigationBar.isTranslucent = true
}
func setTintColor(_ color: UIColor) {
self.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: color]
self.navigationBar.tintColor = color
}
func backgroundColor(_ color: UIColor) {
navigationBar.setBackgroundImage(nil, for: .default)
navigationBar.barTintColor = color
navigationBar.shadowImage = UIImage()
}
}
Storyboard view:
Previews:
Use the appearance API, and barTintColor color.
UINavigationBar.appearance().barTintColor = UIColor.greenColor()
In iOS 15, UIKit has extended the usage of the scrollEdgeAppearance, which by default produces a transparent background, to all navigation bars.
Set scrollEdgeAppearance as below code.
if #available(iOS 15, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = < your tint color >
navigationController?.navigationBar.standardAppearance = appearance;
navigationController?.navigationBar.scrollEdgeAppearance = navigationController?.navigationBar.standardAppearance
}
This version also removes the 1px shadow line under the navigation bar:
Swift 5: Put this in your AppDelegate didFinishLaunchingWithOptions
UINavigationBar.appearance().barTintColor = UIColor.black
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
UINavigationBar.appearance().shadowImage = UIImage()
iOS 8 (swift)
let font: UIFont = UIFont(name: "fontName", size: 17)
let color = UIColor.backColor()
self.navigationController?.navigationBar.topItem?.backBarButtonItem?.setTitleTextAttributes([NSFontAttributeName: font,NSForegroundColorAttributeName: color], forState: .Normal)
If you have customized navigation controller, you can use above code snippet.
So in my case, I've used as following code pieces.
Swift 3.0, XCode 8.1 version
navigationController.navigationBar.barTintColor = UIColor.green
Navigation Bar Text:
navigationController.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.orange]
It is very helpful talks.
Swift 4, iOS 12 and Xcode 10 Update
Just put one line inside viewDidLoad()
navigationController?.navigationBar.barTintColor = UIColor.red
In Swift 2
For changing color in navigation bar,
navigationController?.navigationBar.barTintColor = UIColor.whiteColor()
For changing color in item navigation bar,
navigationController?.navigationBar.tintColor = UIColor.blueColor()
or
navigationController!.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.blueColor()]
Swift 3
UINavigationBar.appearance().barTintColor = UIColor(colorLiteralRed: 51/255, green: 90/255, blue: 149/255, alpha: 1)
This will set your navigation bar color like Facebook bar color :)
Swift 3
Simple one liner that you can use in ViewDidLoad()
//Change Color
self.navigationController?.navigationBar.barTintColor = UIColor.red
//Change Text Color
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
Swift 3 and Swift 4 Compatible Xcode 9
A Better Solution for this to make a Class for common Navigation bars
I have 5 Controllers and each controller title is changed to orange color. As each controller has 5 navigation controllers so i had to change every one color either from inspector or from code.
So i made a class instead of changing every one Navigation bar from code i just assign this class and it worked on all 5 controller Code reuse Ability.
You just have to assign this class to Each controller and thats it.
import UIKit
class NabigationBar: UINavigationBar {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonFeatures()
}
func commonFeatures() {
self.backgroundColor = UIColor.white;
UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor:ColorConstants.orangeTextColor]
}
}
If you're using iOS 13 or 14 and large title, and want to change navigation bar color, use following code:
Refer to barTintColor not applied when NavigationBar is Large Titles
fileprivate func setNavigtionBarItems() {
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithDefaultBackground()
appearance.backgroundColor = .brown
// let naviFont = UIFont(name: "Chalkduster", size: 30) ?? .systemFont(ofSize: 30)
// appearance.titleTextAttributes = [NSAttributedString.Key.font: naviFont]
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
//navigationController?.navigationBar.compactAppearance = appearance
} else {
// Fallback on earlier versions
navigationController?.navigationBar.barTintColor = .brown
}
}
This took me 1 hour to figure out what is wrong in my code:(, since I'm using large title, it is hard to change the tintColor with largeTitle, why apple makes it so complicated, so many lines to just make a tintColor of navigationBar.
iOs 14+
init() {
let appearance = UINavigationBarAppearance()
appearance.shadowColor = .clear // gets also rid of the bottom border of the navigation bar
appearance.configureWithTransparentBackground()
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
}
iOS 10 Swift 3.0
If you don't mind to use swift frameworks then us UINeraida to change navigation background as UIColor or HexColor or UIImage and change navigation back button text programmatically, change complete forground text color.
For UINavigationBar
neraida.navigation.background.color.hexColor("54ad00", isTranslucent: false, viewController: self)
//Change navigation title, backbutton colour
neraida.navigation.foreground.color.uiColor(UIColor.white, viewController: self)
//Change navigation back button title programmatically
neraida.navigation.foreground.backButtonTitle("Custom Title", ViewController: self)
//Apply Background Image to the UINavigationBar
neraida.navigation.background.image("background", edge: (0,0,0,0), barMetrics: .default, isTranslucent: false, viewController: self)
I had to do
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
UINavigationBar.appearance().barStyle = .Black
UINavigationBar.appearance().backgroundColor = UIColor.blueColor()
otherwise the background color wouldn't change
First set the isTranslucent property of navigationBar to false to get the desired colour. Then change the navigationBar colour like this:
#IBOutlet var NavigationBar: UINavigationBar!
NavigationBar.isTranslucent = false
NavigationBar.barTintColor = UIColor (red: 117/255, green: 23/255, blue: 49/255, alpha: 1.0)
Make sure to set the Button State for .normal
extension UINavigationBar {
func makeContent(color: UIColor) {
let attributes: [NSAttributedString.Key: Any]? = [.foregroundColor: color]
self.titleTextAttributes = attributes
self.topItem?.leftBarButtonItem?.setTitleTextAttributes(attributes, for: .normal)
self.topItem?.rightBarButtonItem?.setTitleTextAttributes(attributes, for: .normal)
}
}
P.S iOS 12, Xcode 10.1

Resources