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
Related
I want to use the semantic font styles, but also want to use a custom font. Is this possible so I can continue using .font(.headline) in the code but using my own custom font?
add your custom fonts to your project:
add your custom fonts to your project's info.plist:
Add extension:
extension NSAttributedString.Key {
public enum SourceSansPro: String {
case black = "SourceSansPro-Black"
case blackItalic = "SourceSansPro-BlackIt"
case bold = "SourceSansPro-Bold"
case boldItalic = "SourceSansPro-BoldIt"
case extraLight = "SourceSansPro-ExtraLight"
case extraLightItalic = "SourceSansPro-ExtraLightIt"
case italic = "SourceSansPro-It"
case light = "SourceSansPro-Light"
case lightItalic = "SourceSansPro-LightIt"
case regular = "SourceSansPro-Regular"
case semibold = "SourceSansPro-Semibold"
case semiboldItalic = "SourceSansPro-SemiboldIt"
}
}
Add extension:
extension Font {
static func my(font: NSAttributedString.Key.SourceSansPro, size: CGFloat) -> Font {
return self.custom(font.rawValue, size: size)
}
}
Use:
Text("Title")
.font(.my(font: .bold, size: 17))
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
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.
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:
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)
}
}