It seems quite hard to change the color of the selected UITabBarItem in SwiftUI. To define the design at central place in the app, I tried to use .appearance() to do this, but nothing worked:
// Only effects the unselected items
UITabBar.appearance().unselectedItemTintColor = .red
// No effect, deprecated in iOS 8(!)
UITabBar.appearance().selectedImageTintColor = .cyan
// Only effects the title, not the icon
UITabBarItem.appearance().setTitleTextAttributes([.foregroundColor: UIColor.green], for: .normal)
// No effect
UITabBar.appearance().tintColor = .red
The only thing that works is to not use the appearance proxy but setting the color "manually" in the SwiftUI View definition using .accentColor
struct MainView: View {
var body: some View {
TabView {
SomeView()
.tabItem {
Label("Main", systemImage: "list.dash")
.tint(.green) // No effect
}
.tint(.yellow) // No effect
}
.accentColor(.orange)
// No effect
//.tint(.red)
}
}
However, .accentColor will also be deprecated:
'accentColor' will be deprecated in a future version of iOS: Use the
assets catalog's accent color or View.tint(_:) instead
Using .tint on the TabView has no effect. UITabBarItem has no tint appearance property.
To how to correctly set the icon color without using accentColor (preferably via the appearance)
I’m trying to work this out in Xcode 12.5.1 so I can move to Xcode 13. I have run this app in Xcode 13 Released and that’s how I discovered the problem. I know I’m a little late to the game but before Xcode 13… it wasn’t broken.
In my apps I have custom themes setup in three configurations, summer, fall and winter. Each theme sets the color attributes of the navBar as well as those of the rest of the controls in the app. The theme is chosen on the settings VC from a segmented control.
The setNavbarAttributes() code is run from AppDelegate in didFinishLaunchingWithOptions. It sets the correct navBar attributes when the app launches. In this case the code work as expected. I can verify this by navigating to the other views in the app and observing that the navBar shows the correct color attributes.
The setNavbarAttributes() code also runs when a theme is chosen from the segmented control on the settings VC. Here is the problem. When the theme is changed the navBar color attributes are not carried through to the navBar in the other views. Does anybody know why this isn’t working? I have a not so great workaround by putting the settings VC update code in an extension but that means touching every VC. That doesn't seem right.
It worked fine with my old code shown below but that’s broken in Xcode 13 with UINavigationBarAppearance().
let theNavebarProperties = UINavigationBar.appearance()
theNavebarProperties.barTintColor = Theme.current.navbarColor
theNavebarProperties.isTranslucent = false
theNavebarProperties.titleTextAttributes = [.foregroundColor: Theme.current.accentColor, .font: UIFont.systemFont(ofSize: gNavBarTitleTextSize, weight: .semibold)]
This sets the attributes of the navBar across the app.
class SetNarbar
{
static func setNavbarAttributes()
{
let theAppearance = UINavigationBarAppearance()
theAppearance.configureWithOpaqueBackground()
theAppearance.backgroundColor = Theme.current.navbarColor
theAppearance.titleTextAttributes = [.foregroundColor: Theme.current.accentColor, .font: UIFont.systemFont(ofSize: gNavBarTitleTextSize, weight: .semibold)]
UINavigationBar.appearance().standardAppearance = theAppearance
UINavigationBar.appearance().scrollEdgeAppearance = theAppearance
UINavigationBar.appearance().compactAppearance = theAppearance
}
}
This code is run in the settings VC on viewDidLoad and when the theme selector is tapped.
// Sets the controls on the settings VC to the currently selected theme. I have omitted code that works and does not pertain to setting the navBar.
func updateThemeOnSettingsVC()
{
setNeedsStatusBarAppearanceUpdate()
SetNarbar.setNavbarAttributes()
let navAppearance = UINavigationBarAppearance()
navAppearance.configureWithOpaqueBackground()
navAppearance.backgroundColor = Theme.current.navbarColor
navAppearance.titleTextAttributes = [.foregroundColor: Theme.current.accentColor, .font: UIFont.systemFont(ofSize: gNavBarTitleTextSize, weight: .semibold)]
navigationItem.standardAppearance = navAppearance
navigationItem.scrollEdgeAppearance = navAppearance
navigationItem.compactAppearance = navAppearance
}
I would like to add some space below the tabbar item titles. So I have tried first using storyboards:
The changes are reflected in the storyboard but not at running time in the simulator.
I have tried also programmatically:
UITabBarItem.appearance().titlePositionAdjustment.vertical = -2
or:
tabBar.items!.forEach {
$0.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -2)
}
but also it does not work.
Do you know if I could have some missconfiguration?
Sorry, I didn´t realize that I had some appearance code overriding my code. You was right:
let appearance = tabBar.standardAppearance
appearance.configureWithOpaqueBackground()
So before iOS 13, I was setting the NavigationController.NavigationBar.BarStyle to control the colour of the text int he status bar. But now witht he new UINavigationBarAppearance, is there anyway to control that? I've already tried overriding preferedStatusBarStyle, and ensured my plist contains the setting as well but as far as I can tell this isn't honored if your view controller sits inside a UINavigationController.
So now for iOS 13 I'm setting my navigation bar's appearance using the UINavigationBarAppearance and setting the standardAppearance and scrollEdgeAppearance.
I'm also trying to set NavigationController.NavigationBar.BarStyle to UIBarStyle.Black. It works the first time, but for some reason when I go back and go navigate to the page again setting BarStyle no longer has any effect.
What is the correct way to set the text color?
The barStyle does still work under limited conditions. You can use the barStyle or UIBarAppearance but not both, and you can’t use the barStyle if you use large titles.
Of course you could trivially solve the problem just by subclassing UINavigationController. A bit hacky, but easy.
override var childForStatusBarStyle : UIViewController? {
return self.topViewController
}
What you are expected to do in iOS 13, though, is leave the status bar style alone and let it change automatically in response to the user interface style (light or dark mode). Use a light bar color in light mode and a dark bar color in dark mode and all will be well.
You can change the status bar text color by using this extension:
extension UIApplication {
enum ColorMode {
case dark, light, customColorLowerThanIOS13(_ color: UIColor)
}
func setStatusBarTextColor(_ mode: ColorMode) {
if #available(iOS 13.0, *) {
guard let appDelegate = delegate as? AppDelegate else { return }
var style: UIUserInterfaceStyle
switch mode {
case .dark:
style = .dark
default:
style = .light
}
appDelegate.window?.overrideUserInterfaceStyle = style
} else {
if let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
var color: UIColor
switch mode {
case .dark:
color = .white
case .light:
color = .black
case .customColorLowerThanIOS13(let customColor):
color = customColor
}
statusBar.setValue(color, forKey: "foregroundColor")
}
}
}
}
Using:
UIApplication.shared.setStatusBarTextColor(.dark)
UIApplication.shared.setStatusBarTextColor(.light)
UIApplication.shared.setStatusBarTextColor(.customColorLowerThanIOS13(.red))
In Info.plist, if added the Boolean Property UIViewControllerBasedStatusBarAppearance and set its value to False
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
And you can change StatusBar TextColor by follow way :
UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, true);
In addition , this method should be invoked after View loaded totally . From IOS 13 ,Apple separates the UI from the Controller, which may affect the appearance of the PreferredStatusBarStyle in the Controller. Before this problem is handled, you can use UIApplication.SharedApplication.StatusBarStyle to set it.
Prior to iOS 11, the UINavigationBar buttons and title are being displayed correctly.
Yesterday I downloaded Xcode 9 with iOS 11 and, after building and running without doing changes, both navigation buttons and the title are not being displayed anymore. It shows the UINavigationBar with the correct color I am setting but nothing else.
I tried on different simulators and also I updated an iPhone 7 to iOS 11 beta 5 and the result is the same. Nothing being displayed.
Has someone faced the same problem? I have tried changing different parts of the code and storyboard but nothing affects...
EDIT with screenshots: http://imgur.com/a/Hy46c
Thanks in advance!
For Xcode 9, it appears that it is no longer enough to just set the frame of a custom view that is being injected into the navigationItem titleView. The intrinsic content size of your titleView now must be overriden and set as well.
Here's the code, adjust the width and height to suit your needs:
class NavigationBarTitleView: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: bounds.width - 100, height: 50)
}
...
}
use sizeToFit()! ios 11 automatically sizes it, but ios 10 does not
I had the same issue and for me it was caused by subclassing UITabBarController
Did you set "window,rootViewController = ..." in your code ? Try remove it can fix your problem
I had the same problem in my project where the titles were missing from the navigation bars after updating to Xcode 9 and iOS 11. I solved it by going to the navigation bar of my navigation controller on the storyboard, keeping the Prefers Large Titles unchecked and changing the Title Font under Title Text attributes, which was set by default in Xcode 9 to System 0 to some other option like Caption 1 or Headline. I also changed its children viewcontrollers' navigation bar settings For Large Title to Never instead of Automatic or Always.
I found this code in some inherited codebase, commented it out and everything worked as it did before iOS 11.x.
if (appDelegate.window.rootViewController != self) {
appDelegate.window.rootViewController = self;
}
Try to use:
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
or without appearance proxy setting directly to the current navigationBar...It solves my problem, should Apple changed titleText to clear as default in iOS11...?
Also use this if you want the same look as iOS 10:
if #available(iOS 11, *) {
nav.navigationBar.prefersLargeTitles = false
}
Had the same issue with the navigationButton not displayed. I solved it by setting the renderingMode to .alwaysOriginal. (I didn't use templates)
Swift 3 code:
var img =R.image.smt()?.withRenderingMode(.alwaysOriginal)
I had that same issue and none of the above fixed.
Although, #Justin Vallely lead to me fix it.
All I did was to set a width on the titleView and everything worked just fine!
EDIT:
Every UIViewController has a navigationItem property, and every navigationItem has an optional titleView.
For reference: https://developer.apple.com/documentation/uikit/uinavigationitem/1624935-titleview
In my case, I was using a custom titleView and I think that's the cause of the problem, since Apple changed the API to support the new navigation bar layout.
Based on the Justin Vallely's comment I've reworked the code a little to ensure proper sizing of the view:
class NavigationBarTitleView: UIView {
private var width: CGFloat = 0.0
private var height: CGFloat = 0.0
override init(frame: CGRect) {
super.init(frame: frame)
width = frame.width
height = frame.height
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var intrinsicContentSize: CGSize {
return CGSize(width: width, height: height)
}
}
In my particular case I've used this view as a container to UISearchBar and now it is well sized and worked perfectly with Swift 4 & iOS 11, just as it used to work on previous iOS & Swift versions
We were facing the same issue where the navigation bar color is there but the title and the buttons are not showing up. We have double checked the bar was there by triggering a navigation bar background color change 2 seconds after the navigation controller showed up on the screen, so we know the navigation bar was there and we were adding buttons to the correct instance. Same as the OP, this issue only appears on iOS 11 and not iOS 10, and we are using Swift 3.2 running Xcode 9.1.
After hours of fiddling around, it turns out that presenting a navigation controller, then making it as the UIApplication.shared.delegate.window.rootViewController (after the present animation) caused the issue in our case.
If you just skip the present view controller and make the navigation controller as the root view controller, then everything works fine. Of course, you lose the present animation in the case.