iOS - How to detect whether a font is bold/black/heavy...? - ios

I want to detect the style(bold ,heavy, black) of a font. But I can just detect whether the font is bold.
BOOL isBold = (font.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold)!=0;
There is no black or heavy trait in UIFontDescriptorSymbolicTraits.
A way is to check the font name whether contains 'black' or 'heavy' string, but this seems unreliable.
There is UIFontWeightTrait, but it's just for UIFont systemFontOfSize: weight:
And I want to create my custom font with a style if there is available these style.

To check if it's Heavy or Black:
NSString *fontUsage = font.fontDescriptor.fontAttributes[#"NSCTFontUIUsageAttribute"];
if ([fontUsage isEqualToString:#"CTFontHeavyUsage"]) {
NSLog(#"It's Heavy");
}
else if ([fontUsage isEqualToString:#"CTFontBlackUsage"]) {
NSLog(#"It's Black");
}
The list of other usage options are very simple, just put usage in format "CTFont......Usage", the list I tested are:
//CTFontUltraLightUsage,CTFontThinUsage,CTFontLightUsage,CTFontMediumUsage,CTFontDemiUsage
And How to create a font with usage, like heavy:
UIFontDescriptor *fontDescriptor = [[UIFontDescriptor alloc] initWithFontAttributes:#{#"NSCTFontUIUsageAttribute":#"CTFontHeavyUsage"}];
UIFont *font = [UIFont fontWithDescriptor:fontDescriptor size:17];
Swift3 version for checking:
if let fontUsage = font.fontDescriptor.fontAttributes["NSCTFontUIUsageAttribute"] as? String {
if fontUsage == "CTFontHeavyUsage" {
print("It's Heavy")
}
else if fontUsage == "CTFontBlackUsage" {
print("It's Black")
}
}

Swift Version for detecting Heavy/Black style of font
let fontUsage = font.fontDescriptor.fontAttributes["NSCTFontUIUsageAttribute"] as! String
if fontUsage == "CTFontHeavyUsage"{
print("It is heavy")
}
else if fontUsage == "CTFontBlackUsage"{
print("it's black")
}
and to create font with attributes:
let fontDescriptor = UIFontDescriptor(fontAttributes: ["NSCTFontUIUsageAttribute" : "CTFontHeavyUsage"])
let font = UIFont(descriptor: fontDescriptor, size: 17)

This gives you whether a font is bold or not:
var isBold = label.font.fontDescriptor.symbolicTraits.contains(.traitBold)
Here is some experiement: this gives you the correct answer even if the a bold font is set, or if you set the font's symbolicTraits manually to be bold:

Related

How to set custom bold font programmatically - swift

I am trying to set the font in a UILabel to a custom font but also make it bold without success. My effort so far is below:
CUSTOM FONT:
cell.folderName.font = UIFont.init(name: "American Typewriter", size: 20)
How do I make the above font also bold/ or italic?
This enum can be used for AmericanTypewriter type.
public enum americanTypewriter: String {
case typewriter = "AmericanTypewriter"
case bold = "AmericanTypewriter-Bold"
case condensed = "AmericanTypewriter-Condensed"
case condensedBold = "AmericanTypewriter-CondensedBold"
case condensedLight = "AmericanTypewriter-CondensedLight"
case light = "AmericanTypewriter-Light"
public func font(size: CGFloat) -> UIFont {
return UIFont(name: self.rawValue, size: size)!
}
}
For Bold
cell.folderName.font = UIFont.init(name: "AmericanTypewriter-Bold", size: 20)
Font American Typewriter doesn't have italic style. But it has bold style.
So if you need to set bold font, just use
"AmericanTypewriter-Bold"
All American Typewriter font's styles:
AmericanTypewriter-CondensedBold
AmericanTypewriter-Condensed
AmericanTypewriter-CondensedLight
AmericanTypewriter
AmericanTypewriter-Bold
AmericanTypewriter-Semibold
AmericanTypewriter-Light

How do I get a monospace font that respects acessibility settings

let bodyFontDescriptor = UIFontDescriptor
.preferredFontDescriptor(withTextStyle: UIFontTextStyle.body)
let bodyMonospacedFontDescriptor = bodyFontDescriptor.addingAttributes(
[
UIFontDescriptorFeatureSettingsAttribute: [
[
UIFontFeatureTypeIdentifierKey: kTextSpacingType,
UIFontFeatureSelectorIdentifierKey: kMonospacedTextSelector
]
]
])
let bodyMonospacedFont = UIFont(descriptor: bodyMonospacedFontDescriptor, size: 0.0)
textview.font = bodyMonospacedFont
This produces text with characters of variable width.
I need to get a monospace font without hardcoding courier new
and fixed size.
Deployment target is ios 9.0
Here is an extension to UIFontDescriptor that returns a preferred monospaced font descriptor for a given text style. There is no simple way to get a fully monospaced font using UIFont or UIFontDescriptor. This solution attempts to find a good monospaced font and falls back to Courier if needed.
extension UIFontDescriptor {
static let monoDescriptor: UIFontDescriptor = {
// Attempt to find a good monospaced, non-bold, non-italic font
for family in UIFont.familyNames {
for name in UIFont.fontNames(forFamilyName: family) {
let f = UIFont(name: name, size: 12)!
let fd = f.fontDescriptor
let st = fd.symbolicTraits
if st.contains(.traitMonoSpace) && !st.contains(.traitBold) && !st.contains(.traitItalic) && !st.contains(.traitExpanded) && !st.contains(.traitCondensed) {
return fd
}
}
}
return UIFontDescriptor(name: "Courier", size: 0) // fallback
}()
class func preferredMonoFontDescriptor(withTextStyle style: UIFontTextStyle) -> UIFontDescriptor {
// Use the following line if you need a fully monospaced font
let monoDescriptor = UIFontDescriptor.monoDescriptor
// Use the following two lines if you only need monospaced digits in the font
//let monoDigitFont = UIFont.monospacedDigitSystemFont(ofSize: 0, weight: .regular)
//let monoDescriptor = monoDigitFont.fontDescriptor
// Get the non-monospaced preferred font
let defaultFontDescriptor = preferredFontDescriptor(withTextStyle: style)
// Remove any attributes that specify a font family or name and remove the usage
// This will leave other attributes such as size and weight, etc.
var fontAttrs = defaultFontDescriptor.fontAttributes
fontAttrs.removeValue(forKey: .family)
fontAttrs.removeValue(forKey: .name)
fontAttrs.removeValue(forKey: .init(rawValue: "NSCTFontUIUsageAttribute"))
let monospacedFontDescriptor = monoDescriptor.addingAttributes(fontAttrs)
return monospacedFontDescriptor.withSymbolicTraits(defaultFontDescriptor.symbolicTraits) ?? monospacedFontDescriptor
}
}
Note the comments about whether you need a font that is fully monospaced or a font that just has monospaced digits. Comment/Uncomment those lines to suit your specific needs.
Sample usage:
let bodyMonospacedFont = UIFont(descriptor: .preferredMonoFontDescriptor(withTextStyle: .body), size: 0)
textview.font = bodyMonospacedFont
The following is some test code to confirm that the results of preferredMonoFontDescriptor(withTextStyle:) works properly for all styles:
let textStyles: [UIFontTextStyle] = [ .body, .callout, .caption1, .caption2, .footnote, .headline, .subheadline, .largeTitle, .title1, .title2, .title3 ]
for style in textStyles {
let nfont = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: style), size: 0)
let mfont = UIFont(descriptor: .preferredMonoFontDescriptor(withTextStyle: style), size: 0)
print(style)
print(nfont)
print(mfont)
}
If you compare each pair of results, they have the same size, weight, and style, just a different font.

iOS Monospaced Custom Font

I have a custom font included in my Xcode 7, iOS 9 targeted project. I want to make the font monospaced. I tried this, and didn't work:
let originalFont = UIFont(name: "My Custom Font", size: 18)
let originalFontDescriptor = originalFont!.fontDescriptor()
let fontDescriptorFeatureSettings = [
[
UIFontFeatureTypeIdentifierKey: kNumberSpacingType,
UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector
]
]
let fontDescriptorAttributes = [UIFontDescriptorFeatureSettingsAttribute: fontDescriptorFeatureSettings]
let fontDescriptor = originalFontDescriptor.fontDescriptorByAddingAttributes(fontDescriptorAttributes)
let font = UIFont(descriptor: fontDescriptor, size: 0)
topLabel.font = font
With or without above code, the label displayed in proper custom font. It's just above code doesn't do anything.
My following answer is only making numbers (not the whole font) of an existing font monospaced (if the font supports it)
At least I was searching for making numbers monospaced when finding this Thread. So I hope it will help although it answers another question.
This works just fine, tested on Swift 5 and iOS14+13:
(As long as "your font is supporting the monospaced digits feature".)
extension UIFont {
var monospacedDigitFont: UIFont {
let oldFontDescriptor = fontDescriptor
let newFontDescriptor = oldFontDescriptor.monospacedDigitFontDescriptor
return UIFont(descriptor: newFontDescriptor, size: 0)
}
}
private extension UIFontDescriptor {
var monospacedDigitFontDescriptor: UIFontDescriptor {
let fontDescriptorFeatureSettings = [[UIFontDescriptor.FeatureKey.featureIdentifier: kNumberSpacingType, UIFontDescriptor.FeatureKey.typeIdentifier: kMonospacedNumbersSelector]]
let fontDescriptorAttributes = [UIFontDescriptor.AttributeName.featureSettings: fontDescriptorFeatureSettings]
let fontDescriptor = self.addingAttributes(fontDescriptorAttributes)
return fontDescriptor
}
}
Then you can use it on any label like this:
/// Label with monospacing activated
myLabel.font = myLabel.font.monospacedDigitFontDescriptor
/// Label with monospacing not activated (default is proportional spacing)
myLabel.font = myLabel.font
(source: https://blog.usejournal.com/proportional-vs-monospaced-numbers-when-to-use-which-one-in-order-to-avoid-wiggling-labels-e31b1c83e4d0)
The code you are using is not making font monospaced.
It's tweaking font to render digits in monospace mode. So all with this font digits will have same width.
Below is an example with 4 labels, 1 is using custom font Docis Light, 2nd is Docis Light with monospaced digits on, 3rd is system font of same size, 4th is system font with monospaced digits on:
As you see, this custom font already supports monospace digits feature out of the box with no tweak required.
If you need to use monospaced (not just digits) font, you have to use custom monospaced font (designed to be monospaced) or you can use built-in iOS monospaced fonts such as Courier or Menlo (See all available iOS fonts at http://iosfonts.com/)
This is how they look like with same scenario:
With or without tweaking, they are already monospaced and the digits are monospaced as well.
I answered similar question here, probably, I should just link the answer instead of images but it so much more visual.
Don't forget to import the header file. Hope it will work. This solution is in Objective-C
#import <CoreTextArcView.h>
UIFont *const existingFont = [UIFont preferredFontForTextStyle: UIFontTextStyleBody];
UIFontDescriptor *const existingDescriptor = [existingFont fontDescriptor];
NSDictionary *const fontAttributes = #{
UIFontFeatureTypeIdentifierKey
UIFontDescriptorFeatureSettingsAttribute: #[
#{
UIFontFeatureTypeIdentifierKey: #(kNumberSpacingType),
UIFontFeatureSelectorIdentifierKey: #(kMonospacedNumbersSelector)
}]
};
UIFontDescriptor *const monospacedDescriptor = [existingDescriptor fontDescriptorByAddingAttributes: fontAttributes];
UIFont *const proportionalFont = [UIFont fontWithDescriptor: monospacedDescriptor size: [existingFont pointSize]];

System font for both iOS 8 and iOS 9

I want to support both iOS 8 and iOS 9 systems for my app. And maybe iOS 7. As we know, system font for iOS 7 and 8 is Helvetica Neue. But in iOS 9 system font is San-Francisco. And if you don't set Helvetica font explicitly via [UIFont fontWithName:#"HelveticaNeue" size:15];, but use [UIFont systemFontOfSize:15];, you'll get Helvetica for iOS 7 and 8 and San-Francisco for iOS 9 automatically. And it's great!
For interface builder's labels and buttons you can set thin, ultra thin, medium etc. system fonts. It is great too. But how can I set these thin, ultra, medium system fonts in code, programmatically?
Do I need to create a category with a fork for iOS 9 and previous iOS?
I've created this extension:
import Foundation
import UIKit
enum SystemFontWeight : String {
case UltraLight = "HelveticaNeue-UltraLight"
case Thin = "HelveticaNeue-Thin"
case Light = "HelveticaNeue-Light"
case Regular = "HelveticaNeue"
case Medium = "HelveticaNeue-Medium"
case Semibold = "Helvetica-Bold"
case Bold = "HelveticaNeue-Bold"
case Heavy = "HelveticaNeue-CondensedBold"
case Black = "HelveticaNeue-CondensedBlack"
var weightValue:CGFloat? {
if #available(iOS 8.2, *) {
switch self {
case .UltraLight:
return UIFontWeightUltraLight
case .Thin:
return UIFontWeightThin
case .Light:
return UIFontWeightLight
case .Regular:
return UIFontWeightRegular
case .Medium:
return UIFontWeightMedium
case .Semibold:
return UIFontWeightSemibold
case .Bold:
return UIFontWeightBold
case .Heavy:
return UIFontWeightHeavy
case .Black:
return UIFontWeightBlack
}
} else {
return nil
}
}
}
extension UIFont {
static func systemFontOfSize(fontSize:CGFloat, weight:SystemFontWeight) -> UIFont {
if #available(iOS 8.2, *) {
return UIFont.systemFontOfSize(fontSize, weight: weight.weightValue!)
} else {
// Fallback on earlier versions
return UIFont(name: weight.rawValue, size: fontSize)!
}
}
}
Which makes it possible to apply font like this:
myLabel.font = UIFont.systemFontOfSize(14, weight: .Medium)
This will automatically set the correct font for both iOS 8 and iOS 9.
Use + systemFontOfSize:weight:. It's available for iOS 8 and above.
For iOS 7, interface builder settings will work, and for code you will need to create a UIFontDescriptor with the appropriate weight.
Thanks #Leo Natan. But I want to show a code snippet for copy-paste lovers.
UIFont* systemFont = [UIFont respondsToSelector:#selector(systemFontOfSize:weight:)] ? [UIFont systemFontOfSize:25 weight:UIFontWeightThin] : [UIFont fontWithName:#"HelveticaNeue-Thin" size:25];
Thanks #Antoine for posting great answer for swift. Following is Objective C Similar kind of answer if anybody wants. Implement category for UIFont
UIFont+Cat.m
#import "UIFont+Cat.h"
#implementation UIFont (Cat)
+ (UIFont *)systemFontWithSize:(CGFloat)fontSize weight:(CGFloat)weight {
if ([UIFont respondsToSelector:#selector(systemFontOfSize:weight:)]) {
return [UIFont systemFontOfSize:fontSize weight:weight];
}
NSString *fontName = #"HelveticaNeue";
if (weight == UIFontWeightUltraLight) {
fontName = #"HelveticaNeue-UltraLight";
}
else if (weight == UIFontWeightThin) {
fontName = #"HelveticaNeue-Thin";
}
else if (weight == UIFontWeightLight) {
fontName = #"HelveticaNeue-Light";
}
else if (weight == UIFontWeightRegular) {
fontName = #"HelveticaNeue";
}
else if (weight == UIFontWeightMedium) {
fontName = #"HelveticaNeue-Medium";
}
else if (weight == UIFontWeightSemibold) {
fontName = #"Helvetica-Bold";
}
else if (weight == UIFontWeightBold) {
fontName = #"HelveticaNeue-Bold";
}
else if (weight == UIFontWeightHeavy) {
fontName = #"HelveticaNeue-CondensedBold";
}
else if (weight == UIFontWeightBlack) {
fontName = #"HelveticaNeue-CondensedBlack";
}
return [UIFont fontWithName:fontName size:fontSize];
}
#end

Detect whether a font is bold/italic on iOS?

Given a UIFont or a CTFont, how can I tell whether the font is bold/italic?
iOS7 Font Descriptor
No reason to use Core Text, you can simply ask UIFont for the fontDescriptor.
UIFont *font = [UIFont boldSystemFontOfSize:17.0f];
UIFontDescriptor *fontDescriptor = font.fontDescriptor;
UIFontDescriptorSymbolicTraits fontDescriptorSymbolicTraits = fontDescriptor.symbolicTraits;
BOOL isBold = (fontDescriptorSymbolicTraits & UIFontDescriptorTraitBold) != 0;
Going forward this is probably the easiest way to ask about the traits of a font.
If you want to do this with Swift:
extension UIFont {
var isBold: Bool {
return fontDescriptor.symbolicTraits.contains(.traitBold)
}
var isItalic: Bool {
return fontDescriptor.symbolicTraits.contains(.traitItalic)
}
}
Usage:
let font: UIFont = UIFont.preferredFont(forTextStyle: .headline)
if font.isBold {
print("it's bold..")
}
Looking at the font's name won't always work. Consider the font "Courier Oblique" (which is italic) or "HoeflerText-Black" (which is bold), Neither of those contain "bold" or "italic" in their names.
Given a font as a CTFontRef, the proper way to determine whether it's bold or italic is to use the CTFontGetSymbolicTraits function:
CTFontRef font = CTFontCreateWithName((CFStringRef)#"Courier Oblique", 10, NULL);
CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
BOOL isItalic = ((traits & kCTFontItalicTrait) == kCTFontItalicTrait);
BOOL isBold = ((traits & kCTFontBoldTrait) == kCTFontBoldTrait);
NSLog(#"Italic: %i Bold: %i", isItalic, isBold);
CFRelease(font);
Answer for Swift 3/4 based on Arjan's answer:
extension UIFont {
var isBold: Bool {
return fontDescriptor.symbolicTraits.contains(.traitBold)
}
var isItalic: Bool {
return fontDescriptor.symbolicTraits.contains(.traitItalic)
}
}

Resources