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))
Related
I'm facing a problem, where I need to use different fonts for each language that my application supports.
For example: I need to use "Arial" for Georgian language and "Times new roman" for English language.
Well, obviously there are lot's of ways solving this problem programmatically, but how this could be done using storyboards?
I have a UILabel on the view controller which is loaded from Storyboard. If I choose "Georgian" language in my iPhone settings >> General >> Language & Region >> PREFERRED LANGUAGE ORDER and open the application, the font of the label should be "Arial", but if I choose "English" language in iPhone settings, label's font should be "Times new roman".
NOTE: There shouldn't be any storyboard outlets to the code for the label.
The only thing doing this that comes to my head is to implement #IBInspectable property as UILabel extension and write some part of the code in it:
extension UILabel {
#IBInspectable var adjustFont: Bool {
get { return false } // we don't care about return value
set { // this is where the game starts
if newValue {
switch systemLanguage {
case .Georgian:
self.font = UIFont(name: "Arial", size: 17)
case .English:
self.font = UIFont(name: "Times new roman", size: 17)
}
}
}
}
}
Well, this is kinda solution but I'm looking more elegant/correct way doing this.
Any help/suggestions would be helpful.
Thank you
I'm trying to learn if it is possible to use a custom Arabic and Cyrillic fonts without having to do a switch/if-else on the user's language setting.
I can successfully use my custom font in the app. I'd like to supply a custom Ar/Cy font the same way, and I know I could build it into the app. If I have my font SpecialFont.otf and also supply SpecialFont-CY.otf how would the OS know to use SpecialFontCY.otf when the user is using a Cyrillic language? Ideally the OS will know the user's primary font and would be able to select a font that matches/includes the correct glyphs for the language.
PS. this is not a question on how to use a custom font, I can do that. I want to know how to supply multiple fonts for various languages to fully support the world without writing code like this:
if NSLocale.preferredLanguages.first == "Arabic"
let myFont = UIFont(name:"SpecialFont-AR", size: 17)
else if NSLocale.preferredLanguages.first == "Russian"
let myFont = UIFont(name:"SpecialFont-CY", size: 17)
...etc
Rather than using a UIFont, you want a UIFontDescriptor. With that you can set the font attribute cascadeList, which tells the system what order to select fonts based on glyph availability (i.e. look in SpecialFont, but if you can't find a glyph for ب, try SpecialFont-CY, and then SpecialFont-AR).
The point of a cascade list is to select the correct font for a given glyph. This way, if a string contains Cyrillic, Arabic, and Latin mixed together, it'll still work fine.
For example:
// Start with your base font
let font = UIFont(name:"SpecialFont", size: 17)!
// Create the ordered cascade list.
let cascadeList = [
UIFontDescriptor(fontAttributes: [.name: "SpecialFont-AR"]),
UIFontDescriptor(fontAttributes: [.name: "SpecialFont-CY"]),
]
// Create a new font descriptor based on your existing font, but adding the cascade list
let cascadedFontDescriptor = font.fontDescriptor.addingAttributes([.cascadeList: cascadeList])
// Make a new font base on this descriptor
let cascadedFont = UIFont(descriptor: cascadedFontDescriptor, size: font.pointSize)
This is covered in detail in Creating Apps for a Global Audience (WWDC 2018).
No you can't, but you can define a simple extension to DRY your code:
extension UIFont {
static func preferred(ofSize size: CGFloat) -> UIFont{
switch NSLocale.preferredLanguages.first {
case "Arabic": return UIFont(name:"SpecialFont-AR", size: size)!
case "Russian": return UIFont(name:"SpecialFont-CY", size: size)!
default: return UIFont.systemFont(ofSize: size) // etc.
}
}
}
Now all you have to do is:
let myFont = UIFont.preferred(ofSize: 17)
You will need to check this somehow in order to determine what is the right language to set.
If you don't want to use if/else syntax, you can use Ternary Conditional Operator.
let myFont = (NSLocale.preferredLanguages.first == "Arabic") ? UIFont(name:"SpecialFont-AR", size: 17) : UIFont(name:"SpecialFont-CY", size: 17)
Or more readable, like this:
let fontName = (NSLocale.preferredLanguages.first == "Arabic") ? "SpecialFont-AR" : "SpecialFont-CY"
let myFont = UIFont(name: fontName, 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: