When using a UISegmentedControl, I have a problem fitting all the labels, precisely in Japanese.
I did not notice the same issue with the other language I used at this point.
Here is my code:
interfaceChoice = UISegmentedControl(items: ["白黒モード", "緑赤青・モード"])
for i in 0..<interfaceChoice.subviews.count {
interfaceChoice.subviews[i].tintColor = localColor
for subSubView in interfaceChoice.subviews[i].subviews {
if subSubView is UILabel {
(subSubView as! UILabel).adjustsFontSizeToFitWidth = true
}
}
}
I would hope the line ..adjustsFontSizeToFitWidth.. would sort things out, but it does not work as one can see in the picture below.
Anyone has an idea as what I am doing wrong?
Storyboard example
This is how I did it.
In a story board, drag and drop a segmented control into the title area of the view controller.
Link the segmented control to an IBOutlet in the view controller.
This code
class ViewController: UIViewController {
#IBOutlet weak var interfaceChoice: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
interfaceChoice.removeAllSegments()
interfaceChoice.insertSegment(withTitle: "白黒モード", at: 0, animated: false)
interfaceChoice.insertSegment(withTitle: "緑赤青・モード", at: 1, animated: false)
interfaceChoice.selectedSegmentIndex = 0
let font = UIFont.boldSystemFont(ofSize: 16)
interfaceChoice.setTitleTextAttributes([NSFontAttributeName: font], for: .normal)
interfaceChoice.tintColor = UIColor(red: 27/CGFloat(255), green: 77/CGFloat(255), blue: 102/CGFloat(255), alpha: 1.0)
}
}
Note: I won't pretend to know exactly why it's not behaving for you, but I hope this can help. If it is absolutely imperative to fix your exact situation, I think we might need more information.
Pure code example
This produces the same result as setting it up in the storyboard.
class ViewController: UIViewController {
var interfaceChoice: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
interfaceChoice = UISegmentedControl(items: ["白黒モード", "緑赤青・モード"])
interfaceChoice.selectedSegmentIndex = 0
let font = UIFont.boldSystemFont(ofSize: 16)
interfaceChoice.setTitleTextAttributes([NSFontAttributeName: font], for: .normal)
interfaceChoice.tintColor = UIColor(red: 27/CGFloat(255), green: 77/CGFloat(255), blue: 102/CGFloat(255), alpha: 1.0)
self.navigationItem.titleView = interfaceChoice
}
}
Related
I have a UIButton at bottom of the screen and when the user types in UITextView that button gets attached to the keypad (input accessory view) like the screenshot I have attached (violet arrow mark). Now once the keyboard is dismissed then I want this button to be at the bottom of the screen instead of the input accessory view (check yellow arrow mark flow).
Here is the code I used
override func viewDidLoad() {
super.viewDidLoad()
confirmButtonUI()
subscribeToShowKeyboardNotifications()
// Do any additional setup after loading the view.
}
func confirmButtonUI() {
confirmButton.layer.cornerRadius = 20.0
confirmButton.layer.shadowRadius = 1.0
confirmButton.layer.shadowColor = UIColor(displayP3Red: 33/255, green: 68/255, blue: 27/255, alpha: 0.18).cgColor
confirmButton.layer.backgroundColor = UIColor(displayP3Red: 164/255, green: 208/255, blue: 208/255, alpha: 1).cgColor
confirmButton.isEnabled = false
confirmButton.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
confirmButton.layer.shadowOpacity = 1.0
confirmButton.layer.masksToBounds = false
}
func subscribeToShowKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardWillShow(_:)), name:
UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardWillHide(_:)), name:
UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWillShow(_ notification: Notification) {
let userInfo = notification.userInfo
confirmButton.layer.cornerRadius = 0.0
othersTextField.inputAccessoryView = confirmButton
let keyboardSize = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue
_ = keyboardSize.cgRectValue.height
let animationDuration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
#objc func keyboardWillHide(_ notification: Notification) {
othersTextField.inputAccessoryView = nil
confirmButtonBottomConstrains.constant = 57 //Crash here
let userInfo = notification.userInfo
let animationDuration = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as! Double
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
In this method its crashing on this line " confirmButtonBottomConstrains.constant = 57 "
What i was doing is once the keyboard is dismissing am making inputAccessoryView as nil and then am trying to use the bottom nslayout constraint for the button to set as 57 (like i set in UI) but this line crashing with following message
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Please help me to solve this
Problem
You haven't provided details about whether the view is setup programmatically or in a storyboard. I am assuming it was a storyboard (since I do not see any view initialization).
Also, I am not sure how you got the button to stick as an inputAccessoryView. I couldn't replicate this behavior. In your case, setting a view that is already part of the view hierarchy as an accessory view might be causing the undocumented behavior where existing constraints are removed.
Solution
Instead of using the same button, you can very easily create a new button and assign it as an inputAccessoryView to your UITextField / UITextView.
This would eliminate problems with missing constraints since the original view is never changed.
It would look something like this, (in addition to your disabled button):
Code to setup input accessory view:
override func viewDidLoad() {
super.viewDidLoad()
setupInputAccessory()
othersTextField.delegate = self
}
func setupInputAccessory(){
let width = UIScreen.main.bounds.width
let frame = CGRect(x: 0, y: 0, width: width, height: 44.0)
let btn = UIButton(frame: frame)
btn.backgroundColor = UIColor.purple
btn.setTitleColor(UIColor.white, for: .normal)
btn.setTitle("Confirm", for: .normal)
btn.addTarget(self, action: #selector(accessoryConfirmTapped), for: .touchUpInside)
othersTextField.inputAccessoryView = btn
}
#objc func accessoryConfirmTapped(){
othersTextField.resignFirstResponder()
}
Notes
The above is a code snippet in addition to your existing code. However, with this approach I have removed the following from your code.
Changing the othersTextField.inputAccessoryView in keyboard events.
Confirm Button bottom constraint.
But the gist of it is, I have two separate buttons. One that is setup by the storyboard and the other setup in code and attached to the TextField. I do not share the button with the controller's view and as the input accessory.
I have the complete solution in a GitHub Repo if you want to check it out further.
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 -
I would like to know in which function e.g viewDidLoad etc.. would be the best approach to load custom view properties like image border heights, color, all appearance.
For example I add custom values like these in cellForRowAtIndexPath which I think is not best way to do it:
// corner radius
cell.feedImageView.layer.cornerRadius = 10
// border
cell.feedImageView.layer.borderWidth = 2.0
cell.feedImageView.layer.borderColor = UIColor.black.cgColor
cell.feedImageView.layer.masksToBounds = true
And these navigationBar appearance I load in viewDidLoad and i use it in many views:
navigationController?.navigationBar.barTintColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
self.navigationItem.leftBarButtonItem = nil
navigationController?.navigationBar.isTranslucent = true
let fontDictionary = [ NSForegroundColorAttributeName:UIColor.white ]
self.navigationController?.navigationBar.titleTextAttributes = fontDictionary
self.navigationController?.navigationBar.tintColor = UIColor(red: 0.184, green: 0.996, blue: 0.855, alpha: 1.00)
So the question again, where should I load appearance values? I ask it because right now I am in situation where my image border is loading before image etc..
There are so many functions like viewDidLoadAppearance(), ViewDidload(). I've read many questions where they ask what is difference between them but I still do not know where is best to load appearance.
You have basically 3 different options.
1) You could get or set those values in the viewDidAppear() func, after they have been completely instantiated.
2) You could get or set them in the vieDidLayoutSubviews() function and set a bool to check if your task has been called.
var didConfigureUI = false
func viewDidLayoutSubviews() {
if !didConfigureUI {
didConfigureUI = true
updateUI()
}
}
3) You could get or set them in the viewDidLoad using layoutIfNeeded():
override func viewDidLoad() {
super.viewDidLoad()
xy.view.layoutIfNeeded() // <- This will update your initial view frames
updateUI()
}
And your func to do whatever:
func updateUI() {
//f.e feedImageView.layer.masksToBounds = true
}
And to do it in your cellForRowAtIndexPath, you would do something like this, using the layoutIfNeeded() as well.
cellForRowAtIndexPath {
let cell = tableView.blahblahcell
updateUICell(cell.feedImageView)
}
func updateUICell(imageView: UIImageView) {
imageView.layoutIfNeeded()
imageView.layer.cornerRadius = 10
imageView.layer.borderWidth = 2.0
imageView.layer.borderColor = UIColor.black.cgColor
imageView.layer.masksToBounds = true
}
And to answer the last question about the lifecircle:
The circle is:
init
UIViewController awakeFromNib
loadView // your pictureImageView is loaded here
UIView awakeFromNib
UIViewController viewDidLoad
viewWillAppear
viewDidAppear // the properties of your pictureImageView are available here
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.
class mainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
tabBarController?.delegate = self
self.moreNavigationController.topViewController.view.backgroundColor = UIColor(red: 25.0/255.0, green: 25.0/255.0, blue: 25.0/255.0, alpha: 1.0)
let view = self.moreNavigationController.topViewController.view as UITableView
for cell in view.visibleCells(){
let tableCell = cell as UITableViewCell
tableCell.backgroundColor = UIColor(red: 25.0/255.0, green: 25.0/255.0, blue: 25.0/255.0, alpha: 1.0)
tableCell.textLabel?.textColor = UIColor.whiteColor()
}
}
func tabBarController(tabBarController: UITabBarController, willBeginCustomizingViewControllers viewControllers: [AnyObject]) {
let editView = tabBarController.view.subviews[1] as UIView
editView.backgroundColor = UIColor.redColor()
}
}
Here is my current code. I got the code that's in the 'willBeginCustomizingViewControllers' from an old website (around 4-5 years old) and it worked then apparently but it's not doing anything now. I want to change the background of the "Edit" modal view within the moreNavigationController to the same colour as i have set to the table cells in the viewWillAppear. I've put red for now just to test. Any ideas?
I've done some research, but i cant seem to find anything. I'm not sure its even possible.
There is one more option, but its not so easy. You can call your fifth tab 'more', and place a collection view inside it to access all of the other tabs (just copy apple's 'more' tab, but just start from scratch on a new view controller.).