Issue with setting background and UINavigationbar attributes swift - ios

in my table view controller it works fine and I can configure the view like so:
func configureView() {
self.tableView.backgroundView = UIImageView(image: UIImage(named: "background.jpg"))
//set custom height for tableview row
tableView.rowHeight = 64
//change the font and size of nav bar text
if let navBarFont = UIFont(name: "HelveticaNeue-Thin", size: 20.0) {
let navBarAttributesDictionary: [String: AnyObject]? = [NSForegroundColorAttributeName: UIColor.blackColor(), NSFontAttributeName: navBarFont]
navigationController?.navigationBar.titleTextAttributes = navBarAttributesDictionary
}
}
When I am trying to configure the view in another view controller (with the same navigation controller) it doesn't work and won't change the background or nav bar font, i use this configureview:
func configureView() {
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background.jpg")!)
//change the font and size of nav bar text
if let navBarFont = UIFont(name: "HelveticaNeue-Thin", size: 20.0) {
print("fired")
let navBarAttributesDictionary: [String: AnyObject]? = [NSForegroundColorAttributeName: UIColor.blackColor(), NSFontAttributeName: navBarFont]
UINavigationBar.appearance().tintColor = UIColor.redColor()
navigationController?.navigationBar.titleTextAttributes = navBarAttributesDictionary
}
}
Does anyone have any suggestions on how to debug this or what could be going on? My first view controller is a table view, second a normal view. Thanks
Edit: my segue is of type Show, and my prepare for segue is as follows:
if segue.identifier == "showAlertInfoSegue" {
if let viewController = segue.destinationViewController as? AlertViewController {
viewController.tableAlert = nil
//if the sender is the selected table view row - load in the data from the data struct
if sender?.tag != 1 {
viewController.tableAlert = alertDetail
}
viewController.onDataAvailable = {[weak self]
(data) in
if let weakSelf = self {
weakSelf.doSomethingWithData(data)
}
}
viewController.hidesBottomBarWhenPushed = true
}
}
I also see a peculiar grey shadow appearing on the top nav bar when transitioning, then it dissapears

Related

Change font of prompt in UINavigationController

can someone help me out in changing font, size and color in prompt string on my NavigationController?
In the attachment, I want to modify "Consulenze" string.
Thank you everybody
Edit: I already tried the solution found here but no results.
You can try following ways:
1) In viewDidLoad of your ViewController add this lines:
self.navigationController?.navigationBar.tintColor = UIColor.white
let navigationTitleFont = UIFont(name: "Avenir", size: 20)!
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: navigationTitleFont]
2) You can create completely custom nav bar, just add UIView to the top your view and add all necessary elements - buttons, labels, etc.
Simply add this code in your ViewController. You can change both the Prompt text and color by using this code -
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
for view in self.navigationController?.navigationBar.subviews ?? [] {
let subviews = view.subviews
if subviews.count > 0, let label = subviews[0] as? UILabel {
label.textColor = UIColor.red
label.font = UIFont.systemFont(ofSize: 30)
}
}
}
}
OUTPUT -
Additional -

Can't change UINavigationBar prompt color

I am unable to change the prompt color on my navigation bar. I've tried the code below in viewDidLoad, but nothing happens.
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
Am I missing something? Is the code above wrong?
I was able to make the prompt color white on iOS 11 was setting the barStyle to black. I set the other color attributes (like the desired background color) using the appearance proxy:
myNavbar.barStyle = UIBarStyleBlack; // Objective-C
myNavbar.barStyle = .black // Swift
It seems like you're right about this one. You need to use UIAppearance to style the prompt text on iOS 11.
I've filed radar #34758558 that the titleTextAttributes property just stopped working for prompt in iOS 11.
The good news is that there are a couple of workarounds, which we can uncover by using Xcode's view hierarchy debugger:
// 1. This works, but potentially changes *all* labels in the navigation bar.
// If you want this, it works.
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).textColor = UIColor.white
The prompt is just a UILabel. If we use UIAppearance's whenContainedInInstancesOf:, we can pretty easily update the color the way we want.
If you look closely, you'll notice that there's also a wrapper view on the UILabel. It has its own class that might respond to UIAppearance...
// 2. This is a more precise workaround but it requires using a private class.
if let promptClass = NSClassFromString("_UINavigationBarModernPromptView") as? UIAppearanceContainer.Type
{
UILabel.appearance(whenContainedInInstancesOf: [promptClass]).textColor = UIColor.white
}
I'd advise sticking to the more general solution, since it doesn't use private API. (App review, etc.) Check out what you get with either of these two solutions:
You may use
for view in self.navigationController?.navigationBar.subviews ?? [] {
let subviews = view.subviews
if subviews.count > 0, let label = subviews[0] as? UILabel {
label.textColor = UIColor.white
label.backgroundColor = UIColor.red
}
}
It will be a temporary workaround until they'll fix it
More complicated version to support old and new iOS
func updatePromptUI(for state: State) {
if (state != .Online) {
//workaround for SOFT-7019 (iOS 11 bug - Offline message has transparent background)
if #available(iOS 11.0, *) {
showPromptView()
} else {
showOldPromptView()
}
}
else {
self.navigationItem.prompt = nil
if #available(iOS 11.0, *) {
self.removePromptView()
} else {
self.navigationController?.navigationBar.titleTextAttributes = nil
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:UIColor.lightGray]
}
}
}
private func showOldPromptView() {
self.navigationItem.prompt = "Network Offline. Some actions may be unavailable."
let navbarFont = UIFont.systemFont(ofSize: 16)
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: navbarFont, NSAttributedStringKey.foregroundColor:UIColor.white]
}
private func showPromptView() {
self.navigationItem.prompt = String()
self.removePromptView()
let promptView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 18))
promptView.backgroundColor = .red
let promptLabel = UILabel(frame: CGRect(x: 0, y: 2, width: promptView.frame.width, height: 14))
promptLabel.text = "Network Offline. Some actions may be unavailable."
promptLabel.textColor = .white
promptLabel.textAlignment = .center
promptLabel.font = promptLabel.font.withSize(13)
promptView.addSubview(promptLabel)
self.navigationController?.navigationBar.addSubview(promptView)
}
private func removePromptView() {
for view in self.navigationController?.navigationBar.subviews ?? [] {
view.removeFromSuperview()
}
}
I suggest using a custom UINavigationBar subclass and overriding layoutSubviews:
- (void)layoutSubviews {
[super layoutSubviews];
if (self.topItem.prompt) {
UILabel *promptLabel = [[self recursiveSubviewsOfKind:UILabel.class] selectFirstObjectUsingBlock:^BOOL(UILabel *label) {
return [label.text isEqualToString:self.topItem.prompt];
}];
promptLabel.textColor = self.tintColor;
}
}
Basically I'm enumerating all UILabels in the subview hierarchy and check if their text matches the prompt text. Then we set the textColor to the tintColor (feel free to use a custom color). That way, we don't have to hardcode the private _UINavigationBarModernPromptView class as the prompt label's superview. So the code is be a bit more future-proof.
Converting the code to Swift and implementing the helper methods recursiveSubviewsOfKind: and selectFirstObjectUsingBlock: are left as an exercise to the reader ๐Ÿ˜‰.
You can try this:
import UIKit
class ViewController: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updatePrompt()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updatePrompt()
}
func updatePrompt() {
navigationItem.prompt = " "
for view in navigationController?.navigationBar.subviews ?? [] where NSStringFromClass(view.classForCoder) == "_UINavigationBarModernPromptView" {
if let prompt = view.subviews.first as? UILabel {
prompt.text = "Hello Red Prompt"
prompt.textColor = .red
}
}
navigationItem.title = "This is the title (Another color)"
}
}
Moshe's first answer didn't work for me because it changed the labels inside of system VCs like mail and text compose VCs. I could change the background of those nav bars but that opens up a whole other can of worms. I didn't want to go the private class route so I only changed UILabels contained inside of my custom navigation bar subclass.
UILabel.appearance(whenContainedInInstancesOf: [NavigationBar.self]).textColor = UIColor.white
Try this out:->
navController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor.rawValue: UIColor.red]
I've found next work around for iOS 11.
You need set at viewDidLoad
navigationItem.prompt = UINavigationController.fakeUniqueText
and after that put next thing
navigationController?.promptLabel(completion: { label in
label?.textColor = .white
label?.font = Font.regularFont(size: .p12)
})
extension UINavigationController {
public static let fakeUniqueText = "\n\n\n\n\n"
func promptLabel(completion: #escaping (UILabel?) -> Void) {
gloabalThread(after: 0.5) { [weak self] in
guard let `self` = self else {
return
}
let label = self.findPromptLabel(at: self.navigationBar)
mainThread {
completion(label)
}
}
}
func findPromptLabel(at view: UIView) -> UILabel? {
if let label = view as? UILabel {
if label.text == UINavigationController.fakeUniqueText {
return label
}
}
var label: UILabel?
view.subviews.forEach { subview in
if let promptLabel = findPromptLabel(at: subview) {
label = promptLabel
}
}
return label
}
}
public func mainThread(_ completion: #escaping SimpleCompletion) {
DispatchQueue.main.async(execute: completion)
}
public func gloabalThread(after: Double, completion: #escaping SimpleCompletion) {
DispatchQueue.global().asyncAfter(deadline: .now() + after) {
completion()
}
}

Customize the searchController of navigationItem , but doesn't work with iOS 11

I have searched the similar question UISearchController iOS 11 customizationbut al the methods in the comments couldn't help me out.So I want to ask it again.
I used the following codes to set the appearance of the searchBar.
Extension the UISearchBr to get the textField and placeHolderLabel:
extension UISearchBar{
var textField: UITextField?{
if let textField = self.value(forKey: "searchField") as? UITextField {
return textField
}
return nil
}
var placehloderLabel:UILabel?{
if let placeholderLabel = textField?.value(forKey: "placeholderLabel") as? UILabel{
return placeholderLabel
}
return nil
}
}
Customize a subclass of UISearchController:
class CustomSearchController:UISearchController{
override init(searchResultsController: UIViewController?) {
super.init(searchResultsController: searchResultsController)
self.definesPresentationContext = true
self.dimsBackgroundDuringPresentation = false
self.hidesNavigationBarDuringPresentation = true
self.searchBar.searchBarStyle = .minimal
self.searchBar.placeholder = "ๆœ็ดขๆญŒๅ•ๅ†…ๆญŒๆ›ฒ"
self.searchBar.textField?.textColor = UIColor.white
self.searchBar.placehloderLabel?.textColor = .white
self.searchBar.placehloderLabel?.font = UIFont.systemFont(ofSize: 15)
}
Set prefersLargeTitles UINavigationBar.appearance().prefersLargeTitles = true
If navigationItem.searchController = searchController and the result is as follows (the appearance of the searchBar DOESN'T make change):
But if I set navigationItem.titleView = searchController.searchBar,it makes:
Does the iOS 11 permit developers to change the searchBar's
appearance?If yes,I wonder how to customize it ?Any
point is appreciated.Thanks!
Working with color and UINavigationItem's search controller
This code is in the AppDelegate class.
UISearchBar.appearance().tintColor = UIColor(named: "Button") // using this to set the text color of the 'Cancel' button since the search bar ignores the global tint color property for some reason
if #available(iOS 11.0, *) {
// Search bar placeholder text color
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).attributedPlaceholder = NSAttributedString(string: "Search", attributes: [NSForegroundColorAttributeName: UIColor.white])
// Search bar text color
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes = [NSForegroundColorAttributeName: UIColor.red]
// Insertion cursor color
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).tintColor = UIColor.red
} else {
// Fallback on earlier versions
}
// Search bar clear icon
UISearchBar.appearance().setImage(UIImage(named: "clear"), for: .clear, state: .normal)

UINavigationBar : Add SearchBar as TitleView

I want to add uisearchbar in place of title of controller.
I am facing 2 problems currently.
1)
I do not know from where this gray background is coming. Please check this picture.
2) I need this searchbar in other inner screens also. But when I push another controller this searchbar is removed.
Here is my code :
// Seachbar Container View
let searchBarContainer = ERView(frame: CGRectMake(0,0,280,44))
searchBarContainer.autoresizingMask = [.FlexibleWidth]
searchBarContainer.backgroundColor = UIColor.clearColor()
// Search Bar
let searchBar = ERSearchBar(frame: searchBarContainer.bounds)
searchBarContainer.addSubview(searchBar)
// Add View as Title View in Navigation Bar
self.navigationController!.navigationBar.topItem?.titleView = searchBarContainer
Here is Code of my UISearchBar Class
func commonInit() -> Void {
// Update UI
if let searchField = self.valueForKey("searchField") as? UITextField{
// To change background color
searchField.backgroundColor = UIColor.appNavigationBarDarkRedColor()
// Tint Color
if let leftViewRef = searchField.leftView {
leftViewRef.tintColor = UIColor.whiteColor()
}else if let imgLeftView = searchField.leftView as? UIImageView{
imgLeftView.tintColor = UIColor.whiteColor()
}
// Font Color
searchField.font = UIFont.robotoRegularFont(WithSize: 14.0)
// Text Color
searchField.textColor = UIColor.whiteColor()
// PlaceHolder Attributes
searchField.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [NSForegroundColorAttributeName:UIColor.whiteColor()])
}
self.setImage(UIImage(named: "ic_search_white"), forSearchBarIcon: UISearchBarIcon.Search, state: UIControlState.Normal)
// To change placeholder text color
}
Please someone help me here.
For 1) Set the searchBarStyle property to minimal. This will provides no default background color in UIsearchBar.
2)I'm not quite sure what'd you mean by "inner screen". But if what you want is a Search Bar that can work across different view controllers, UISearchController is the one you're looking for.
You can fine the Apple sample code here

Change TabBarItem title font to bold in Swift for iOS

I'm trying to set the font weight of a selected tab bar item to bold font. It seems as it has no effect. Any idea what is wrong. forState: .Normal works as expected, forState: .Selected has no effect.
let tabBarItem0 = tabBar.items![0] as! UITabBarItem
var selectedImage0 : UIImage = UIImage(named:"ic_tabbar_item_one")!.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
var fontLight:UIFont = UIFont(name: "HelveticaNeue-UltraLight", size: 12)!
var fontBold:UIFont = UIFont(name: "HelveticaNeue-Bold", size: 12)!
tabBarItem0.image = unselectedImage0
tabBarItem0.selectedImage = selectedImage0
tabBarItem0.title = "Overview"
tabBarItem0.setTitleTextAttributes(
[
NSForegroundColorAttributeName: UIColor.whiteColor(),
NSFontAttributeName: fontLight
], forState: .Normal)
tabBarItem0.setTitleTextAttributes(
[
NSForegroundColorAttributeName: UIColor.whiteColor(),
NSFontAttributeName: fontBold
], forState: UIControlState.Selected)
FOUND THE SOLUTION (Swift 3, XCode 8.1)
In Storyboard, give a unique Tag to each UITabBarItem you have: For every tab -> Select it and go to it's "Attributes Inspector" -> Give each one a unique number in the "Tag" field but you should not use zero (I used 1 through 4).
This sets us up for later, to identify which tab is pressed.
Create a new subclass of UITabBarController and then assign it: FILE -> New File -> iOS Cocoa Touch -> create a Subclass of UITabBarController. Assign the new .swift file to your
UITabBarController under "Identity Inspector."
We will need custom logic in our UITabBarController.
Create a new subclass of UITabBarItem, assign the same file to all of your UITabBarItems: FILE -> New File -> iOS Cocoa Touch -> create a Subclass of UITabBarItem and assign the same one to all of your tabs.
We will need a shared custom logic in our tab bar items.
Add this code to your UITabBarItem subclass, it sets up the initial state (primary tab bold, the rest unselected) and will allow for programmatic tab changes as well:
class MyUITabBarItemSubclass: UITabBarItem {
//choose initial state fonts and weights here
let normalTitleFont = UIFont.systemFont(ofSize: 12, weight: UIFontWeightRegular)
let selectedTitleFont = UIFont.systemFont(ofSize: 12, weight: UIFontWeightBold)
//choose initial state colors here
let normalTitleColor = UIColor.gray
let selectedTitleColor = UIColor.black
//assigns the proper initial state logic when each tab instantiates
override func awakeFromNib() {
super.awakeFromNib()
//this tag # should be your primary tab's Tag*
if self.tag == 1 {
self.setTitleTextAttributes([NSFontAttributeName: selectedTitleFont, NSForegroundColorAttributeName: selectedTitleColor], for: UIControlState.normal)
} else {
self.setTitleTextAttributes([NSFontAttributeName: normalTitleFont, NSForegroundColorAttributeName: normalTitleColor], for: UIControlState.normal)
}
}
}
Here we set up the initial state so that the tabs are set correctly when the app opens up, we'll take care of the physical tab presses in the next subclass.
Add this code to your UITabBarController subclass, it's the logic for assigning the correct states as you press on the tabs.
class MyUITabBarControllerSubclass: UITabBarController {
//choose normal and selected fonts here
let normalTitleFont = UIFont.systemFont(ofSize: 12, weight: UIFontWeightRegular)
let selectedTitleFont = UIFont.systemFont(ofSize: 12, weight: UIFontWeightBold)
//choose normal and selected colors here
let normalTitleColor = UIColor.gray
let selectedTitleColor = UIColor.black
//the following is a delegate method from the UITabBar protocol that's available
//to UITabBarController automatically. It sends us information every
//time a tab is pressed. Since we Tagged our tabs earlier, we'll know which one was pressed,
//and pass that identifier into a function to set our button states for us
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
setButtonStates(itemTag: item.tag)
}
//the function takes the tabBar.tag as an Int
func setButtonStates (itemTag: Int) {
//making an array of all the tabs
let tabs = self.tabBar.items
//looping through and setting the states
var x = 0
while x < (tabs?.count)! {
if tabs?[x].tag == itemTag {
tabs?[x].setTitleTextAttributes([NSFontAttributeName: selectedTitleFont, NSForegroundColorAttributeName: selectedTitleColor], for: UIControlState.normal)
} else {
tabs?[x].setTitleTextAttributes([NSFontAttributeName: normalTitleFont, NSForegroundColorAttributeName: normalTitleColor], for: UIControlState.normal)
}
x += 1
}
}
}
It looks like this was such a pain because for some reason the tabs do not recognize state changes into ".Selected". We had to do everything by working with .Normal states only - basically detecting the state changes ourselves.
You can programmatically change the tabs and still detect state changes by... I'll update this later if anyone has an interest, just ask.
Hope this helped!
UITabBarItem.appearance().setTitleTextAttributes(
[NSFontAttributeName: UIFont(name:"your_font_name", size:11)!,
NSForegroundColorAttributeName: UIColor(rgb: 0x929292)],
forState: .Normal)
I've faced the same issue when tried to change font of selected item. Looks like titleTextAttributes' font parameter is only useful when setting them to normal state. That's why I implemented UITabBarControllerDelegate where I update attributes for currently selected item. You should call updateSelection() method after UITabBarControllers loadView() too. Or you can call updateSelection() method in overridden selectedItem setter.
extension TabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
updateSelection()
}
func updateSelection() {
let normalFont = Fonts.Lato.light.withSize(10)
let selectedFont = Fonts.Lato.bold.withSize(10)
viewControllers?.forEach {
let selected = $0 == self.selectedViewController
$0.tabBarItem.setTitleTextAttributes([.font: selected ? selectedFont : normalFont], for: .normal)
}
}
}
Build Settings\Swift Language Version: 4.1
General\Deployment Target: 10.3
import UIKit
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let attrsNormal = [NSAttributedStringKey.foregroundColor : UIColor.black,
NSAttributedStringKey.font : UIFont(name: "Arial", size: 14)!]
UITabBarItem.appearance().setTitleTextAttributes(attrsNormal,
for: UIControlState.normal)
let attrsSelected = [NSAttributedStringKey.foregroundColor : UIColor.red,
NSAttributedStringKey.font : UIFont(name: "Arial", size: 14)!]
UITabBarItem.appearance().setTitleTextAttributes(attrsSelected,
for: UIControlState.selected)
}
...
}
When I try to change font of selected tab by using UITabBarItem but it doesn't work. Adoption of delegate is suggested to get informed when tab changes. I just put pieces together.
private class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
override func setViewControllers(_ viewControllers: [UIViewController]?, animated: Bool) {
super.setViewControllers(viewControllers, animated: animated)
updateTabBarAppearance()
}
}
extension MyTabBarController: UITabBarControllerDelegate {
public func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
updateTabBarAppearance()
}
func updateTabBarAppearance() {
self.tabBar.tintColor = .red
let unselectedLabelFont = UIFont.systemFont(ofSize: 18, weight: .regular)
let selectedLabelFont = UIFont.systemFont(ofSize: 20, weight: .semibold)
let labelColor = UIColor.init(red: 38/255.0, green: 38/255.0, blue: 38/255.0, alpha: 1.0)
let selectedIconColor = UIColor.init(red: 8/255.0, green: 8/255.0, blue: 8/255.0, alpha: 1.0)
viewControllers?.forEach {
let isSelected = $0 == self.selectedViewController
let selectedFont = isSelected ? selectedLabelFont : unselectedLabelFont
$0.tabBarItem.setTitleTextAttributes([.font: selectedFont, .foregroundColor: labelColor], for: .normal)
}
}
}
To set the TitleTextAttribute, you should use the appearance proxy like: [UIBarItem appearance]
The problem is that the state of tabBarItem0 is not changed to Selected. Because this is UITabBarItem which represents a single element of a UITabBar. So, you can not directly change the status using UITabBarItem API. You have to change it state by assigning selectedItem.
This information is gained from documentation and I suggest all programmers to have skills like this. Hopefully, this will help.

Resources