How can I pick a random UIFont by name (with a constant size)?
There isn't much more to this question but it seems SO wants more from me so here is some meaningless twaddle.
create an array of all the fonts you want and then use arc4rand to pick a random object at index to use as your font.
Try this:
extension UIFont {
static var random: UIFont {
return UIFont(name: UIFont.familyNames.randomElement()!, size: CGFloat.random(in: 5...15))!
}
}
Then use this extension as usual:
let font = UIFont.random
This gives a fully random font with random size (you can of course eliminate this feature if it is not needed)
Related
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)
What is the iOS 13 system font?
Before iOS 13 I used SFUIDisplay font.
UIFont(name: ".SFUIDisplay-Light", size: UIFont.systemFontSize)
But on iOS 13 it doesn't work.
This bug is so BS. The only way to get around it is by using the systemFont(ofSize:weight:) method directly. AKA, you cannot get the system font using the method UIFont(name:size:), you ll just get Times New Roman for some funny reason. Apple devs must be messing with us. So for the original question above you must use the following:
UIFont(systemFont:UIFont.systemFontSize, weight: .light)
For my situation, I ran into this bug making an extension for UIFont to return the same font in a different size, which must work with custom and system fonts. In order for it to work on xcode 11 ios 13, I had to add a silly check to see if fontName contains ".SF".
extension UIFont {
func forSize(_ pointSize: CGFloat) -> UIFont {
if !fontName.contains(".SF"), let font = UIFont(name: fontName, size: pointSize) {
return font
}
return UIFont.systemFont(ofSize: pointSize, weight: weight)
}
}
If you are aiming to use the system font, you don't really have to worry about its name, you should let the system to do it for you.
let font = UIFont.systemFont(ofSize: UIFont.systemFontSize)
At this point, whenever the system font changes, it will automatically updated.
Moreover
I use a lot of custom fonts. I need to do it
Actually, you could do it without mentioning the font name in case you want to use the system font. For example, you could implement a function that returns the proper font as:
func getFont(name: String = "", size: CGFloat = UIFont.systemFontSize) -> UIFont {
// system font
let defaultFont = UIFont.systemFont(ofSize: UIFont.systemFontSize)
if name.isEmpty {
return defaultFont
}
return UIFont(name: name, size: size) ?? defaultFont
}
For using the system font, call it: getFont(). Otherwise, call it with mentioning the name of the font: getFont(name: ".SFUIDisplay-Light").
However, you might think of doing something like this to get the system font name and then use it:
let systemFontName = UIFont.systemFont(ofSize: UIFont.systemFontSize).fontName
getFont(name: systemFontName)
I'd say it's meaningless since the UIFont.systemFont automatically detects the system font name without the need of mentioning it.
font-family: ".SFCompactText-Regular"; font-weight: normal; font-style: normal
It's "San Francisco (SF) Pro and Compact" font. You can check here https://developer.apple.com/fonts/
Create a font using systemFont(ofSize: CGFloat) -> UIFont.
Then get the fontName and familyName of that font.
Print the above and you have the answer for the current iOS; so run it on iOS 13.
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]];
I've read around for different solutions but nothing seems to work. This code creates a nil exception:
[NSFontAttributeName: UIFont(name: "Raleway-SemiBold", size: 16)!]
I have the fonts installed properly and they show up correctly in the app (target is set).
I tried to add the application provided fonts in the plist but nothing happened. I can't edit the items in the array: (they are item0 : string : Raleway-SemiBold.tff).
So basically I'm stuck... Sometimes Swift and Apple environments are great for a programmer, other times (most of the time), they are sooo faulty and need so many workarounds to reach the expected results.
Many thanks in advance for any help.
You're getting an exception because UIFont(name: "Raleway-SemiBold", size: 16) returns nil and you're force-unwrapping it with !.
Instead, use conditional unwrapping:
if let font = UIFont(name: "Raleway-SemiBold", size: 16) {
let attributes = [NSFontAttributeName: font]
// do something with attributes
} else {
// The font "Raleway-SemiBold" is not found
}
You can use UIFont's familyNames() and fontNamesForFamilyName(_:) methods to get the exact string required.
Swift 4
if let font = UIFont(name: "Raleway-SemiBold", size: 16) {
let attributes = [NSAttributedStringKey.font: font]
// do something with attributes
} else {
// The font "Raleway-SemiBold" is not found
}
You Just have to write the correct string name of your font.
Don't write the name that is font file name. (like bodoni-mt-bold.ttf its the file name i have downloaded from any site).
To find out the exact font name follow the image below.
Go to your label select it and go to custom font and then see the name of your custom font in its family. if your custom font name is there then copy that name and past it as a string where u wanna use it. (Note you can't copy font name text you have to write else where then past it)
For Swift 3, here's an update that worked for me:
First you'll set up the font and then create a textAttribute with the NSFontAttributeName:
let font = UIFont(name: "Raleway-SemiBold", size: 16)
textAttribute = [NSFontAttributeName: font as Any, NSForegroundColorAttributeName: UIColor.black]
You can then apply textAttribute to your label, textfield etc.
I want to resize the font-size in some UITextViews. That works fine with an outlet collection and this code:
for (UITextView *view in self.viewsToResize) {
if ([view respondsToSelector:#selector(setFont:)]) {
[view setFont:[UIFont systemFontOfSize:view.font.pointSize + 5]];
}
}
But my problem is, that not every textView uses the systemFont in normal weight, some of them are in bold weight. Is it possible to get the font-weight? With a po view.font in the debug area I can see everything I need:
$11 = 0x0c596ea0 <UICFFont: 0xc596ea0> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12px
But how can I access the font-weight?
Using a second outlet collection for the bold views could solve my problem. But I'm wondering that I found nothing to get only the font-weight.
I have figured out how to get the font weights, you have to spelunk down to Core Text:
let ctFont = font as CTFont
let traits = CTFontCopyTraits(ctFont)
let dict = traits as Dictionary
if let weightNumber = dict[kCTFontWeightTrait] as? NSNumber {
print(weightNumber.doubleValue)
}
Enjoy!
UIFont does not have a bold/italic/... property, so you will have to rely on the font name only.
This will be a problem if you don't know which fonts will be used.
In the case you know that you will use eg. only Helvetica you can try this:
UIFont *font = textview.font;
if([font.fontName isEqualToString:#"Helvetica-Bold"])
NSLog(#"It's Bold!");
Alternatively you can search font.fontName for the word "bold"/"medium"/"light" etc., but that's not a guarantee you will get something from every available font:
if ([font.fontName rangeOfString:#"bold" options:NSCaseInsensitiveSearch].location == NSNotFound) {
NSLog(#"font is not bold");
} else {
NSLog(#"font is bold!");
}
// if font.fontName contains "medium"....
// if font.fontName contains "italic"....
Check http://iosfonts.com/ for the available font names.
But my problem is, that not every textView uses the systemFont in
normal weight, some of them are in bold weight.
If you want to use Bold System Font then you can simply use
[UIFont boldSystemFontOfSize:15.0];
However, I am still thinking of that special case in which you need to use font-weight.
Update :
There is nothing in the UIFont Class using which you can get font-weight directly. You can take a look at UIFont Class Reference.
Only thing that you can do is to get the font-name and try to find out the "bold" sub-string in the font name. If any match found that means font-weight of that specific font is "bold".
But, still this is not the most efficient method.
You can get a UIFontDescriptor for a font using the fontDescriptor method. Then you get the fontAttributes dictionary of the UIFontDescriptor, get the UIFontDescriptorTraitsAttribute from the dictionary which returns another dictionary, then read the UIFontWeightTrait from that dictionary. Not tested.
Now it's tested: Doesn't work. fontAttributes always returns a dictionary with two keys for font name and font size, and that's it. I suppose "this doesn't work" is also an answer when something should work according to the documentation...
You can try symbolicTraits, but that's not useful either: It returns "bold" only if the whole font family is bold.