I need to change the font of all the UILables, UITextViews, UITextFields, UIButtons (title), in the app based on the content of the text or title. How can I do this globally?.
I did try to create an extension for each and make the changes in layoutSubviews(), but this only worked for UILabels.
extension UILabel {
override open func layoutSubviews() {
super.layoutSubviews()
let text = self.text ?? ""
var fontName = THEME_FONT_NAME
if (!text.isAlphanumeric) {
self.font = UIFont.systemFont(ofSize: self.font.pointSize, weight: fontWeight)
return
}
self.font = UIFont(name: fontName, size: self.font.pointSize)
}}
Are you using firebase, then specify fonts in remote config. fetch those fonts and create some utility class with static property font. e.g
class Utils{
var font: UIFont = UIFont("fontFetched") ?? default font
}
like:
label.text.font = font
and use it wherever you implement fields and buttons
I'm using TextStyles in my app to support dynamic fonts. I have the challenge to change the Font for each TextStyle. So for example the TextStyle.body should be MyAwesomeBODYFont and the TextStyle.headline should be MyAwesomeHeadlineFont. And this for the entire app. Setting the font for the whole app won't work because I need several Fonts for the different styles.
Is it possible to override these TextStyles somehow with custom fonts for the entire app and not for each label separately?
What I tried:
Setting the font for the appearance proxy of UILabel in general works fine:
let labelAppearance = UILabel.appearance()
let fontMetrics = UIFontMetrics(forTextStyle: .body)
labelAppearance.font = fontMetrics.scaledFont(for: myAwesomeBodyFont)
But this overrides all labels not matter what TextStyle they use.
After that I tried to check for the TextStyle but it crashes with a nil pointer exception for the UILabel.appearance().font or does not even go into the if-block.
let labelAppearance = UILabel.appearance()
if let textStyle = labelAppearance.font.fontDescriptor.object(forKey: UIFontDescriptor.AttributeName.textStyle) as? UIFont.TextStyle {
// this would be the place to check for the TextStyle and use the corresponding font
let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
labelAppearance.font = fontMetrics.scaledFont(for: mayAwesomeBodyFont)
}
Because the appearance of UILabel does not have a font set.
You cannot directly "set" a custom font for Text Styles.
You can get font size for text style and then could use custom family.
let systemDynamicFontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleBody)
let size = systemDynamicFontDescriptor.pointSize
let font = UIFont(name: MyAwesomeBODYFont, size: size)
For iOS 11+ There is scaledFont()
You can make this font variable static and could use it everywhere in app.
You could check it out this solution too: https://stackoverflow.com/a/42235227/4846167
I ended up creating a subclass of UILabel and let all my labels inherit from it. This way you can set the class in InterfaceBuilder and/or create the custom class in code.
This is the DynamicCustomFontLabel class:
import UIKit
class DynamicCustomFontLabel: UILabel {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
initCustomFont()
}
override init(frame: CGRect) {
super.init(frame: frame)
initCustomFont()
}
private func initCustomFont() {
if let textStyle = font.fontDescriptor.object(forKey: UIFontDescriptor.AttributeName.textStyle) as? UIFont.TextStyle {
let fontMetrics = UIFontMetrics(forTextStyle: textStyle)
var customFont: UIFont?
switch textStyle {
case .body:
customFont = UIFont(name: "MyAwesomeBODYFont", size: 21)
case .headline:
customFont = UIFont(name: "MyAwesomeHeadlineFont", size: 48)
// all other cases...
default:
return
}
guard let font = customFont else {
fatalError("Failed to load a custom font! Make sure the font file is included in the project and the font is added to the Info.plist.")
}
self.font = fontMetrics.scaledFont(for: font)
}
}
}
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 have UITextField with longer text in it set as placeholder. What I want is for this placeholder text to adjust its font size when the width of field is too small.
I already tried this solution described in other posts (programmatically and in IB)
self.fieldTest.adjustsFontSizeToFitWidth = true
self.fieldTest.minimumFontSize = 10.0
What am I missing here?
You can create a subclass of UITextField:
class AutoSizeTextField: UITextField {
override func layoutSubviews() {
super.layoutSubviews()
for subview in subviews {
if let label = subview as? UILabel {
label.minimumScaleFactor = 0.3
label.adjustsFontSizeToFitWidth = true
}
}
}
}
Or you can just add some code in your view controller's viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
for subview in fieldTest.subviews {
if let label = subview as? UILabel {
label.minimumScaleFactor = 0.3
label.adjustsFontSizeToFitWidth = true
}
}
}
Here you go :
_myTextField.placeholder = #"SomeTextSomeTextSome";
UILabel *label = [_myTextField valueForKey:#"_placeholderLabel"];
label.adjustsFontSizeToFitWidth = YES;
Cheers!!
Based on answer 9: in Storyboard go to the identity inspector tab of the text field element, and under the "User Defined Runtime Attributes" section, add the following:
Here's a solution that depends on the undocumented fact that the UITextField has a child UILabel (actually UITextFieldLabel) to render the placeholder. The advantage of this solution over some others is that it degrades gracefully should Apple's implementation change. It also doesn't make assumptions about the existence of undocumented ivars.
Basically we extend UILabel via a category. If we see ourselves being parented to a UITextField then we turn on adjustFontSizeToFitWidth.
#interface UILabel (TS)
#end
#implementation UILabel (TS)
- (void) didMoveToSuperview
{
[super didMoveToSuperview];
if ( [self.superview isKindOfClass: [UITextField class]] ) {
self.adjustsFontSizeToFitWidth = YES;
}
}
#end
After reviewing the class reference for UITextField's, it seems that adjustsFontSizeToFitWidth only affects the the text property of the UITextField and not the placeholder property. While I don't know off the top of my head a way to get the placeholder to respond to adjustsFontSizeToFitWidth, I can suggest two hacky ideas that may give you the appearance that you want. Just be aware that I'm not near a Mac right now so I haven't tested these ideas:
1:
Since a placeholder is just text with a 70% gray color, you could set the label's text property to be whatever you need it to be, and then implement the UITextFieldDelegate's textFieldShouldBeginEditing method to clear the text and change the color back to normal. You would also have to implement the textFieldShouldClear and textFieldDidEndEditing methods to replace the pseudo-placeholder back in the UITextField and change the text color back to 70% gray.
2:
In viewWillAppear you could set the UITextField's text to what your placeholder should be, create a UIFont object and set it equal to the UITextField's font property, clear the UITextField's text, and set the placeholder to be an NSAttributedString with the font object as a property. Here's an example of what I mean:
-(void)viewWillAppear:(BOOL) animated {
[super viewWillAppear:animated];
someTextField.adjustsFontSizeToFitWidth = YES;
someTextField.text = #"placeholderText";
UIFont *font = someTextField.font;
someTextField.text = nil;
NSDictionary *attributes = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
NSAttributedString *placeholderString= [[NSAttributedString alloc] initWithString:#"placeholderText" attributes:attributes];
someTextField.placeholder = placeholderString;
}
Edit: Just noticed the swift tag. I wrote my code in Objective-C, but you should be able to easily translate it to Swift.
Try using attributed placeholder instead of normal place holder
Try this
let attributedplaceholder = NSAttributedString(string: "placeholdertext", attributes: [NSFontAttributeName: UIFont(name: "FontName", size: 10)!])
self.fieldTest.attributedPlaceholder = attributedplaceholder
You can add additional attributes to the placeholder like textcolor and other
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
for subView in fieldTest.subviews{
if subView .isKind(of: UILabel.self){
let label = subView as! UILabel
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.2
}
}
}
Swift
Feel free to improve the extension - pretty sure there is a more elegant way to iterate over the subviews.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tfCountryCode.allSubviewsOfClass(UILabel.self).forEach {
$0.adjustsFontSizeToFitWidth = true
$0.minimumScaleFactor = 0.5
}
}
extension UIView {
func allSubviewsOfClass<K: UIView>(_ clazz: K.Type) -> [K] {
var matches = [K]()
if subviews.isEmpty { return matches }
matches.append(contentsOf: subviews.filter { $0 is K } as! [K])
let matchesInSubviews = subviews.flatMap { return $0.allSubviewsOfClass(clazz) }
matches.append(contentsOf: matchesInSubviews.flatMap { $0 })
return matches
}
}
My solution:
if let label = yourTextField.value(forKey: "placeholderLabel") as? UILabel {
label.adjustsFontSizeToFitWidth = true
}
And don't forget this:
yourTextField.minimumFontSize = 10 // 10 is an example, pass your minimumFontSize
Tested and run perfectly.
You can use these two solution:
1.If You have fixed font size if Textfield size is less than placeholder text:
let placeholderString = testTF.placeholder
print(placeholderString!)
let font = UIFont(name: (testTF.font?.fontName)!, size: 16)!
let fontAttributes = [NSFontAttributeName: font]
let size = (placeholderString! as NSString).sizeWithAttributes(fontAttributes)
print(size)
print(testTF.frame.size.width)
if(size.width > testTF.frame.size.width)
{
let font = UIFont(name: (testTF.font?.fontName)!, size: 4)!
let attributes = [
NSForegroundColorAttributeName: UIColor.lightGrayColor(),
NSFontAttributeName : font]
testTF.attributedPlaceholder = NSAttributedString(string: placeholderString!,
attributes:attributes)
}
else
{
let attributes = [
NSForegroundColorAttributeName: UIColor.lightGrayColor(),
NSFontAttributeName : font]
testTF.attributedPlaceholder = NSAttributedString(string: placeholderString!,
attributes:attributes)
}
2) If you want dynamic font size than you just check the above condition for width of textfield and placeholder text size.width. if the placeholder text size is greater than textfield size than create one label inside the textfield and set minimum font on that.
if(size.width > testTF.frame.size.width)
{
placeholder = UILabel(frame: CGRect( x: 0, y: 0, width: testTF.bounds.width, height: testTF.bounds.height))
placeholder.text = placeholderString
placeholder.numberOfLines = 1;
//placeholder.minimumScaleFactor = 8;
placeholder.adjustsFontSizeToFitWidth = true
placeholder.textColor = UIColor.grayColor()
placeholder.hidden = !testTF.text!.isEmpty
placeholder.textAlignment = .Center
testTF.addSubview(placeholder)
}
In Swift
yourTextField.subviews
.filter { $0 is UILabel }
.flatMap { $0 as? UILabel }
.forEach {
$0.adjustsFontSizeToFitWidth = true
$0.minimumScaleFactor = 0.5
}
it works :)
Try this: It's working fine without any issues:
yourTextField.placeholder = "Adjust placeHolder text for textFields iOS"
let label = yourTextField.value(forKey: "_placeholderLabel") as? UILabel
label?.adjustsFontSizeToFitWidth = true
The whole approach or shrinking font size to fit is misguided in the day and age of accessibility.
Firstly you have zero business specifying text size in the first place, let alone shrinking that further: you have to rely on the accessibility API.
Thus if the placeholder is likely to not fit it has to be placed
as a UILabel preceding the UITextField. The placeholders are supposed to be SHORT and fit without clippage.
To determine if it's clipped I guess you could use - (CGRect)placeholderRectForBounds:(CGRect)bounds; but then you are in
murky waters of using an API which Apple says you should only override (but not call yourself even though it's probably meaningful and safe
within the confines of didlayoutsubviews method[s])
If placeholder text is dynamic (server served) dump it into a UILabel.
I want to show Button title like this
but my output is comming like this even after centralising the title
frmbtn is the outlet of button And the Code I am Using for this is
frmbtn.setTitle("From Station\nSTN\nSTATION NAME", forState: UIControlState.Normal)
frmbtn.titleLabel!.numberOfLines = 3
frmbtn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Center
And Can we put different text sizes in the button title ?
Here is whole as you want.
import UIKit
class ViewController: UIViewController {
#IBOutlet var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
var str : NSMutableAttributedString = NSMutableAttributedString(string: "From station\nSTN\nStation Name")
str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(20), range: NSRange(location: 13,length: 3))
button.setAttributedTitle(str, forState: UIControlState.Normal)
button.titleLabel!.lineBreakMode = .ByWordWrapping
button.titleLabel?.textAlignment = .Center
button.titleLabel?.adjustsFontSizeToFitWidth = true
}
}
in Easy way you can also give space like this:
frmbtn.setTitle("From Station\n STN\nSTATION NAME", forState: UIControlState.Normal) \\give here space after \n and see the result
frmbtn.titleLabel!.numberOfLines = 3
frmbtn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Center
You have to set the text alignment on the titleLabel itself. Also you can change the font size as part of the font property.
scanButton.titleLabel!.numberOfLines = 3
scanButton.titleLabel!.textAlignment = NSTextAlignment.Center;
scanButton.titleLabel!.font = UIFont.systemFontOfSize(40);
To set a multiline title on a UIButton, check this forum post which describes changing the button label.
myButton.titleLabel.textAlignment = UITextAlignmentCenter;
myButton.titleLabel.lineBreakMode = UILineBreakModeCharacterWrap;
[myButton setTitle:#"From Station STN station name" forState:UIControlStateNormal];