I have a strange issue which I don't really understand.
I have two views, one should have a black title and the other should have a white title.
The issue that I am experiencing is that I can set the color ONCE and not change it back.
So when I go to the view that has the white title from the view with the black title and then go back, the title does not change back to black.
code for white title in viewWillAppear:
self.navigationController?.navigationBar.barStyle = .black
self.navigationController?.navigationBar.tintColor = .white
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
code for black title in viewWillAppear:
self.navigationController?.navigationBar.isHidden = false
self.navigationController?.navigationBar.barStyle = .default
self.navigationController?.navigationBar.tintColor = UIColor.blue
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
Why does it not change back, when I am clearly setting a new color?
EDIT: adding the complete code
Black title view:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.isHidden = false
self.navigationController?.navigationBar.barStyle = .default
self.navigationController?.navigationBar.tintColor = hexStringToUIColor(hex: "4CAF50")
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
self.navigationItem.title = listData.name
clearStatusBarColor()
let editButton = UIBarButtonItem(title: "Edit", style: .plain, target: self, action: #selector(tapToEdit))
let sortImg = UIImage(named: "sort")
sortingButton = UIBarButtonItem(image: sortImg, style: .plain, target: self, action: #selector(tapToSort))
self.navigationItem.rightBarButtonItems = [sortingButton!, editButton]
self.navigationController?.navigationBar.setBackgroundImage(nil, for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = nil
// get updated Data
if User.active.hasListUpdated {
// return with new gameEntries -> Update
listData = User.active.allLists![listDataIndex] // To keep upToDate data!
listEntries = listData.list_entries!
gameEntries = listEntries.compactMap({ (entry: ListEntry) -> GameEntry in
return GameEntry(game: entry.game, platform: nil, platform_id: entry.platform, rating: Int(entry.rating ?? 0), review: nil, notes: entry.description)
})
listTable.reloadData()
}
// Sorting
if hasSortChanged {
hasSortChanged = false
sortList(sort: sortingOption, order: sortingOrder)
}
}
White title view:
override func viewWillAppear(_ animated: Bool) {
if !isPreviewing {
self.navigationController?.navigationBar.isHidden = false
self.navigationController?.navigationBar.barStyle = .black
self.navigationController?.navigationBar.tintColor = .white
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
// MARK: Clear StatusBar
clearStatusBarColor()
if transparentNav {
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for:UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.title = nil
} else {
self.navigationController?.navigationBar.setBackgroundImage(nil, for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = nil
self.title = game.name!
}
}
// MARK: NavigationBar
let button = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(showOptions))
self.navigationItem.rightBarButtonItem = button
// Check if game should have new review or rating
if User.active.hasMainScreenUpdated {
// Update rating
updateUserRating()
// update review
updateUserReviewStatus()
}
// Update lists!
if User.active.hasListUpdated {
updateListsStatus()
}
}
If you are changing the nav bar colors in different view controllers, I recommend you to have a subclass of UIViewController and handle the navbar changes through that. Here's an example for your case.
class CustomUIViewController: UIViewController {
override func didMove(toParentViewController parent: UIViewController?) {
super.didMove(toParentViewController: parent)
if parent == nil {
if SettingsManager.LastBarColor == .default {
self.setLightBars()
}
else {
self.setDarkBars()
}
}
}
func setDarkBars() {
SettingsManager.LastBarColor = .lightContent
UIApplication.shared.statusBarStyle = UIStatusBarStyle.lightContent
tabBarController?.tabBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
}
func setLightBars() {
SettingsManager.LastBarColor = .default
UIApplication.shared.statusBarStyle = UIStatusBarStyle.default
tabBarController?.tabBar.tintColor = UIColor.Black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:UIColor.Black]
navigationController?.navigationBar.barTintColor = UIColor.white
navigationItem.titleView?.tintColor = UIColor.Black
}
}
class SettingsManager {
class var LastBarColor: UIStatusBarStyle = .default
}
And in your view controller use CustomUIViewController, call setDarkBars() or setLightBars() in your viewWillAppear() function.
You can use a custom UINavigationController class then override pushViewController function to set what you need on the navigationBar.
The viewWillAppear method has a lot of code here.
class MyNavigationViewController: UINavigationController {
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
super.pushViewController(viewController, animated: animated)
self.updateForVC(viewController: viewController)
}
func updateForVC(viewController: UIViewController) {
//DO WHATEVER YOU WHANT HERE, title, img, etc
var color = UIColor.black
if viewController.isKind(of: MyClass.self) {
color = UIColor.white
}
self.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: color]
}
}
Try pushViewController to navigate,It is working for me
if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
if let navigator = self.navigationController {
navigator.pushViewController(viewController, animated: true)
viewController.title = ""
}
}
Related
In my Swift project, I added a sublayer to UINavigationController.
But after adding this GAGradientLayer, I can't see the navigation title text or back button.
The weird thing is that in the view hierarchy, the CAGradientLayer(which was added as sublayer) is behind the title and button.
I tried to reload navigationController layer with LayoutIfNeeded, setNeedsLayout or setNeedsDisplay but nothing worked.
And I also just tried to change the navigation title but it doesn't work.
(Actually The text of navigation title is loaded on the view controller behind, so I don't want to change this on this VC.)
So, How can I show my navigation title text and button with CAGradientlayer above?
Here's the screenshot
Here's the codes needed
import UIKit
import SnapKit
class BulletinBoardViewController: UIViewController {
// ...
var backgroundGradientLayer: CAGradientLayer?
let bulletinBoardView = BulletinBoardView()
// MARK: - Lifecycles
override func viewDidLoad() {
super.viewDidLoad()
setBulletinBoardView()
setCells()
}
override func viewWillAppear(_ animated: Bool) {
setupBackgroundLayer()
}
override func viewWillDisappear(_ animated: Bool) {
self.backgroundGradientLayer?.removeFromSuperlayer()
}
// MARK: - Helpers
func setupBackgroundLayer() {
DispatchQueue.main.async {
if let backgroundGradientLayer = self.backgroundGradientLayer {
backgroundGradientLayer.frame = CGRect(x: 0, y: -59, width: 500, height: 103)
self.navigationController?.navigationBar.layer.addSublayer(backgroundGradientLayer)
}
}
}
func setBulletinBoardView() {
self.view.addSubview(bulletinBoardView)
bulletinBoardView.snp.makeConstraints { make in
make.right.left.top.equalTo(self.view.safeAreaLayoutGuide)
make.bottom.equalTo(self.view)
}
}
// ...
}
The origin navigation controller setting is below
class MainPageViewController: UIViewController {
// ...
func setupNav() {
navigationController?.navigationBar.tintColor = .black
navigationItem.rightBarButtonItem = listButton
navigationItem.leftBarButtonItem = settingButton
let backBarButtonItem = UIBarButtonItem(title: "",
style: .plain,
target: self,
action: nil)
self.navigationItem.backBarButtonItem = backBarButtonItem
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .systemGray3
appearance.titleTextAttributes = [NSAttributedString.Key.font: UIFont(name: AppFontName.bold, size: 20)!]
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance =
navigationController?.navigationBar.standardAppearance
}
// ...
}
I know this question has been asked before but nothing worked for me and I had to ask it again.
I want an image as my back button in navigation bar, just want to change the appearance of the back button. I don't want to add a button and add selectors for it.
I tried the following code:
let backImage = UIImage(named: "Back_button")
let backAppearance = UIBarButtonItem.appearance()
backAppearance.setBackButtonBackgroundImage(backImage, for: .normal, barMetrics: .default)
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItem.Style.plain, target: nil, action: nil)
I also tried setting the back image and back mask using storyboard but both these approaches place a black circle on my back image.
I tried setting another image as back mask by setting its alpha content equal to zero using the code but it didn't work either.
please help.
let backButton = UIBarButtonItem()
backButton.title = "Back"
backButton.image = UIImage(named: "Back_button")
self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton
You can do this to customize your Back button. And you don't have to worry about adding selectors.
This code works with Swift 5.
let backButton: UIButton = UIButton()
backButton.setImage(UIImage(named: "back"), for: UIControl.State())
backButton.addTarget(self, action:#selector(SearchResultsViewController.onBack), for: UIControl.Event.touchUpInside)
let leftBarButtonItem = UIBarButtonItem(customView: backButton)
navigationItem.leftBarButtonItem = leftBarButtonItem
I used this code to customize the back button on only one of my views:
self.navigationController?.navigationBar.topItem?.backButtonTitle = ""
let backButton = UIBarButtonItem(image: UIImage(named: "back"), style: .plain, target: self, action: #selector(goBack))
navigationItem.leftBarButtonItem = backButton
#objc func goBack() {
self.navigationController?.popViewController(animated: true)
}
Create a custom class for define navigation bar traits
Create an extension to UINavigationController for configure it
import UIKit
private final class MyNavigationBarTraits {
public var backIndicatorImage: UIImage?
public var backIndicatorTransitionMaskImage: UIImage?
public func apply(to navigationBar: UINavigationBar) {
navigationBar.backIndicatorImage = backIndicatorImage
navigationBar.backIndicatorTransitionMaskImage = backIndicatorTransitionMaskImage
}
public init(navigationBar: UINavigationBar) {
backIndicatorImage = navigationBar.backIndicatorImage
backIndicatorTransitionMaskImage = navigationBar.backIndicatorTransitionMaskImage
}
}
public typealias Callback<T> = (_: T) -> Void
public extension UINavigationController {
private struct AssociationKeys {
static var navigationBarTraits = "ws_nc_navigationBarTraits"
}
private var navigationBarTraits: MyNavigationBarTraits? {
get {
return objc_getAssociatedObject(self, &AssociationKeys.navigationBarTraits) as? MyNavigationBarTraits
}
set {
objc_setAssociatedObject(self, &AssociationKeys.navigationBarTraits, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func configureBar(block: Callback<UINavigationBar>) {
navigationBarTraits = MyNavigationBarTraits(navigationBar: navigationBar)
block(navigationBar)
}
func resetBar() {
navigationBarTraits?.apply(to: navigationBar)
navigationBarTraits = .none
}
}
And then you can configure your navigation bar in your ViewController's viewWillAppear (for example tintColor)
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.configureBar { navigationBar in
// You can customize your navigation bar in here!
navigationBar.tintColor = .red
}
}
If you want to use this customization just in one View Controller you should reset bar in your View Controller's viewWillDisappear
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.resetBar()
}
Simply Add Below Methods in Your ViewController :
func setLeftBarBackItem() {
let leftBarBackItem = UIBarButtonItem(image: #imageLiteral(resourceName: "imgBack"), style: .plain, target: self, action: #selector(self.clickToBtnBackItem(_:)))
self.navigationItem.leftBarButtonItem = leftBarBackItem
}
func clickToBtnBackItem(_ sender: UIBarButtonItem) {
view.endEditing(true)
_ = navigationController?.popViewController(animated: true)
}
func setTranspertNavigation()
{
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.view.backgroundColor = .clear
}
Inside Your ViewController's ViewDidLoad Method, Set backButton As :
self.navigationController?.isNavigationBarHidden = false
AppDelegate.shared().setupNavigationBar()
setLeftBarBackItem()
setTranspertNavigation()
self.title = "Title Here"
im facing the problem of setting the font-color of the title of one ViewController in swift and resetting it when it disappears. Currently I'm able to set the color from black to white with:
override func viewDidLoad() {
let textAttributes = [NSAttributedStringKey.foregroundColor:UIColor.white]
navigationController?.navigationBar.titleTextAttributes = textAttributes
}
override func viewWillDisappear(_ animated: Bool) {
let textAttributes = [NSAttributedStringKey.foregroundColor:UIColor.black]
navigationController?.navigationBar.titleTextAttributes = textAttributes
}
when I try resetting with UIColor.black it doesn't change anything.
when I try to set the whole appearance there is no change at all.
override func viewDidLoad() {
UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
}
override func viewWillDisappear(_ animated: Bool) {
UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.black]
}
How can I still achieve this?
Using Xcode 10.0
Swift 4
Instead of adding the code to viewDidLoad(), add it into viewDidAppear(_:), i.e
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
navigationController?.navigationBar.titleTextAttributes = textAttributes
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black]
navigationController?.navigationBar.titleTextAttributes = textAttributes
}
Thanks everybody for your help.
I got it working for me with another function:
override func willMove(toParentViewController parent: UIViewController?) {
var textAttributes: [NSAttributedString.Key : Any]?
if parent == nil{ // navigating back
textAttributes = [NSAttributedStringKey.foregroundColor:UIColor.black]
}else{
textAttributes = [NSAttributedStringKey.foregroundColor:UIColor.white]
}
navigationController?.navigationBar.titleTextAttributes = textAttributes
}
This functions is also called when a view is building up.
This solution worked for me and the colors are already set when the view is displayed.
I'm open for pro/cons responds to this.
you can use this function viewWillAppear in your code on any view
func navigationBarProperties(vc:UIViewController, title:String){
vc.navigationController!.navigationBar.isHidden = false
vc.navigationController!.navigationBar.isTranslucent = false
// text color
vc.navigationController!.navigationBar.tintColor = UIColor.white
// bar color
vc.navigationController!.navigationBar.barTintColor = UIColor.black
let isFont = UIFont.init(name: "Helvetica-bold", size: 15)
vc.navigationItem.title = title
vc.navigationController!.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) ,NSAttributedStringKey.font: isFont!]
}
override open func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//---nav bar customization----//
navigationBarProperties(vc: self, title: "Home")
}
Subclass UINavigationController and assign it to your navigationController:
class CustomNavigationController : UINavigationController {
let usualTextAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.red]
let customTextAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.blue]
private func updateTitleColorIfNeeded() {
if topViewController is MyCustomViewController {
navigationBar.titleTextAttributes = customTextAttributes
} else {
navigationBar.titleTextAttributes = usualTextAttributes
}
}
override func popViewController(animated: Bool) -> UIViewController? {
updateTitleColorIfNeeded()
return super.popViewController(animated: animated)
}
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
updateTitleColorIfNeeded()
super.pushViewController(viewController, animated: animated)
}
}
If MyCustomViewController is root of the navigation controller, then set it's initial title color in viewDidLoad:
class MyCustomViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.yellow]
}
}
This is a bit different question than the previously asked. So be nice.
The entire app has a navigation bar with different colors.
The entire app has few options in its navigation bar. They all are same.
What am I expecting?
I have created this extension for UINavigationController and able to change the navigation bar's background color as per the view controller I will be.
extension UINavigationController {
func updateNavigationBar(withViewControllerID identifier: String?) {
setupNavigationBarButtons()
if identifier == kFirstVCIdentifier {
self.navigationBar.tintColor = .white
self.navigationBar.barTintColor = .red
} else if identifier == kSecondVCIdentifier {
self.navigationBar.tintColor = .white
self.navigationBar.barTintColor = .green
} else if identifier == kThirdVCIdentifier {
self.navigationBar.tintColor = .white
self.navigationBar.barTintColor = .blue
}
self.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
self.navigationBar.isTranslucent = false
self.navigationBar.clipsToBounds = false
self.navigationBar.shadowImage = UIImage.init()
self.view.backgroundColor = .clear
}
internal func setupNavigationBarButtons() {
let barButtonItemSettings = UIBarButtonItem.init(title: "Settings", style: .plain, target: self, action: #selector(actionSettings))
let barButtonItemSpace = UIBarButtonItem.init(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let barButtonItemLogOut = UIBarButtonItem.init(title: "LogOut", style: .plain, target: self, action: #selector(actionLogOut))
let buttons = [barButtonItemSettings, barButtonItemSpace, barButtonItemLogOut]
self.navigationItem.rightBarButtonItems = buttons
}
#objc internal func actionSettings() {
print("Settings")
}
#objc internal func actionLogOut() {
print("LogOut")
}
}
then, I am trying to add UIBarButton within that extension but they are not showing up. So I need to fix it. And if it will be visible, how do I get its actions call so that I can handle UIBarButton's touch event in the entire app. Properly? No patch, please.
I am using it like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.updateNavigationBar(withViewControllerID: self.restorationIdentifier?)
}
Try this and Make extension for UINavigationItem.
extension UINaviationItem {
func setupNavigationBarButtons()
{
let barButtonItemSettings = UIBarButtonItem.init(title: "Settings", style: .plain, target: self, action: #selector(actionSettings))
let barButtonItemSpace = UIBarButtonItem.init(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let barButtonItemLogOut = UIBarButtonItem.init(title: "LogOut", style: .plain, target: self, action: #selector(actionLogOut))
let buttons = [barButtonItemSettings, barButtonItemSpace, barButtonItemLogOut]
self.rightBarButtonItems = buttons
}
#objc func actionSettings() {
print("Settings")
if let current = UIApplication.shared.delegate?.window
{
var viewcontroller = current!.rootViewController
if(viewcontroller is UINavigationController){
viewcontroller = (viewcontroller as! UINavigationController).visibleViewController
print( "currentviewcontroller is %#/",viewcontroller )
}
}
}
#objc func actionLogOut() {
print("LogOut")
}
}
self.navigationController?.updateNavigationBar(withViewControllerID: "second")
self.navigationItem.setupNavigationBarButtons()
I have a firstViewController which has a UISegmentedControl and SegmentedControl has two tabs button. its all working fine but when i go to next SecondViewController. On SecondViewController there is a link for webView when i push to that webView & then back to SecondViewController and then back to firstViewController by tapping back button. The SegmentedControl leave its position & it moves below the NavigationController
firstViewController Code is :
override func viewDidLoad() {
super.viewDidLoad()
setupMenuBar()
setupViewControllerUI()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupNavigationBarUI()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
// to avoid getting a black navigationBar while transition
navigationController?.navigationBar.barTintColor = UIColor.white
self.navigationController?.navigationBar.setBackgroundImage(UIColor.white.convertImage(), for: UIBarMetrics.default)
navigationController!.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont.appThemeRegularFontWithSize(20.0), NSForegroundColorAttributeName: UIColor.lightGray]
}
// MARK: - UIViewController Helper Method
func setupViewControllerUI() {
if isFirstVC {
AppUtility.switchToViewController(viewController: firstViewController!, in: self)
segmentedControl.selectedSegmentIndex = 0
} else {
LoadingViewController.sharedLoader.showLoading(self.navigationController!)
AppUtility.switchToViewController(viewController: secondViewController, in: self)
segmentedControl.selectedSegmentIndex = 1
}
}
func setupMenuBar() {
SegmentedControlContainerView.backgroundColor = UIColor.ButtonColorWithAlpha(1.0)
segmentedControl.tintColor = UIColor.white
}
func setupNavigationBarUI() {
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
navigationController?.navigationBar.barTintColor = UIColor.ButtonColorWithAlpha(1.0)
navigationController?.navigationBar.setBackgroundImage(UIColor.ButtonColorWithAlpha(1.0).convertImage(), for: UIBarMetrics.default)
navigationController?.navigationBar.shadowImage = UIColor.white.withAlphaComponent(0.0).convertImage()
navigationController?.view.backgroundColor = UIColor.ButtonColorWithAlpha(1.0)
segmentedControl.setTitle("first", forSegmentAt: 1)
segmentedControl.setTitle("second", forSegmentAt: 0)
self.navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.barStyle = UIBarStyle.default
self.title = "Screen Name"
navigationController?.navigationBar.setNeedsDisplay()
}