Different fonts for each language using storyboards in iOS application - ios

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

Related

iOS using custom Arabic/Cyrillic fonts automatically

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)

System font renders as Times New Roman on iOS 13

I have some UILabel with the default system font. But when I install my app on iPad or iPhone with iOS 13.1 the fonts change to something like Times New Roman! Why does this happen? I am sure the label's text is Plain and the font is System. How can I fix this issue?
PS: I have downloaded all SF fonts from Apple web site, and still no luck!
I found the solution, the problem comes with detecting the current label's font. I changed:
descriptions.font = UIFont(name: (descriptions.font?.fontName)!, size: 22)
to
descriptions.font = UIFont.systemFont(ofSize: 22)
and problem solved.
Use UIFontDescriptor
I was having the same issue on iOS 13. Fixed it by using fontDescriptor instead of fontName. I have UILabel in my storyboard connected to its view controller via IBOutlet with font as Text Styles - Callout.
#IBOutlet weak var lblText: UILabel!
Below one didn't worked as expected and showing Times New Roman font:
let font = UIFont.init(name: lblText.font.fontName, size: 50.0)!
lblText.font = font
lblText.text = "Times Coding :)"
Solution using UIFontDescriptor:
let font = UIFont.init(descriptor: lblText.font.fontDescriptor, size: 50.0)
lblText.font = font
lblText.text = "Times Coding :)"
This way it will pick the font you set to a label in your storyboard, you don't need to hardcode the font name.
It seems like Apple is pushing to use the initializer with the weightage. Passing it with the name seems to break it ".SFUI-Regular".
The workaround for this is to use the function with weight like this : UIFont(systemFont:UIFont.systemFontSize, weight: .regular).

What is the system font of iOS13?

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.

Change font in UITextInput

I'm trying to develop a keyboard and it should use a different font then the default one. Here's the line:
override func textDidChange(textInput: UITextInput?) {
textInput.font = UIFont(name: "font name", size: 30)
}
But it doesn't work.... How can I fix it?
Thanks.
Follow this steps to use custom Fonts
Drag and drop your font into Xcode
Choose options for adding these files:
Copy items if needed
Create groups
Add to targets:(your app name)
In your Info.plist file add a key named: Fonts provided by application, which is an array
Customize your font:
myUILabel.text = "demoText"
myUILabel.font = UIFont(name: "myCustomFont.ttf", size: 25)

Attributed string with custom fonts in storyboard does not load correctly

We are using custom fonts in our project. It works well in Xcode 5. In Xcode 6, it works in plain text, attributed string in code. But those attributed strings set in storyboard all revert to Helvetica when running on simulator or device, although they look all right in storyboard.
I'm not sure if it's a bug of Xcode 6 or iOS 8 SDK, or the way to use custom fonts is changed in Xcode 6 / iOS 8?
The simplest answer that worked for is to drag the fonts into FontBook. If the fonts are in your project but not in your computer's FontBook, IB sometimes has trouble finding it. Weird, but has worked for me on several occasions.
The fix for me was to use an IBDesignable class:
import UIKit
#IBDesignable class TIFAttributedLabel: UILabel {
#IBInspectable var fontSize: CGFloat = 13.0
#IBInspectable var fontFamily: String = "DIN Light"
override func awakeFromNib() {
var attrString = NSMutableAttributedString(attributedString: self.attributedText)
attrString.addAttribute(NSFontAttributeName, value: UIFont(name: self.fontFamily, size: self.fontSize)!, range: NSMakeRange(0, attrString.length))
self.attributedText = attrString
}
}
Giving you this in the Interface Builder:
You can set up your attributedstring just as you normal do, but you'll have to set your fontsize and fontfamily once again in the new available properties.
As the Interface Builder is working with the custom font by default, this results in a what you see is what you get, which I prefer when building apps.
Note
The reason I'm using this instead of just the plain version is that I'm setting properties on the attributed label like the linespacing, which are not available when using the plain style.
You can add custom fonts to font book.
Step1: Click on manage fonts. It opens the font book.
Step2: Click on plus and add your fonts.
Next time when you click on font with attributed text newly added font also will show in the list. But make sure your custom font added in info.plist and bundle resources.
My solution is a bit of a work around. The real solution is for apple to fix Interface Builder.
With it you can mark all the bold and italic text in interface builder using a system font, then at runtime render your custom font. May not be optimal in all cases.
NSMutableAttributedString* ApplyCustomFont(NSAttributedString *attributedText,
UIFont* boldFont,
UIFont* italicFont,
UIFont* boldItalicFont,
UIFont* regularFont)
{
NSMutableAttributedString *attrib = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText];
[attrib beginEditing];
[attrib enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attrib.length) options:0
usingBlock:^(id value, NSRange range, BOOL *stop)
{
if (value)
{
UIFont *oldFont = (UIFont *)value;
NSLog(#"%#",oldFont.fontName);
[attrib removeAttribute:NSFontAttributeName range:range];
if([oldFont.fontName rangeOfString:#"BoldItalic"].location != NSNotFound && boldItalicFont != nil)
[attrib addAttribute:NSFontAttributeName value:boldItalicFont range:range];
else if([oldFont.fontName rangeOfString:#"Italic"].location != NSNotFound && italicFont != nil)
[attrib addAttribute:NSFontAttributeName value:italicFont range:range];
else if([oldFont.fontName rangeOfString:#"Bold"].location != NSNotFound && boldFont != nil)
[attrib addAttribute:NSFontAttributeName value:boldFont range:range];
else if(regularFont != nil)
[attrib addAttribute:NSFontAttributeName value:regularFont range:range];
}
}];
[attrib endEditing];
return attrib;
}
Inspired by this post
Thanks to this thread, I've come to this solution:
private let fontMapping = [
"HelveticaNeue-Medium": "ITCAvantGardePro-Md",
"HelveticaNeue": "ITCAvantGardePro-Bk",
"HelveticaNeue-Bold": "ITCAvantGardePro-Demi",
]
func switchFontFamily(string: NSAttributedString) -> NSAttributedString {
var result = NSMutableAttributedString(attributedString: string)
string.enumerateAttribute(NSFontAttributeName, inRange: NSRange(location: 0, length: string.length), options: nil) { (font, range, _) in
if let font = font as? UIFont {
result.removeAttribute(NSFontAttributeName, range: range)
result.addAttribute(NSFontAttributeName, value: UIFont(name: fontMapping[font.fontName]!, size: font.pointSize)!, range: range)
}
}
return result
}
I have struggled with this bug: UILabel displays correctly in IB with custom font but does not display correctly on device or simulator (font is included in the project and is used in plain UILabels).
Finally found Attributed String Creator on (Mac) App Store. Generates code to be placed in your app in the appropriate place. Fantastic.
I am not the creator, just a happy user.
Met the same problem: the attribute font set for TextView in storyboard didn't work in run time with XCode 6.1 & iOS 8 SDK.
This is how I solved this issue, might be a workaround for you:
open the attribute inspector of your textview, change text to
"Plain"
click on the cross to delete the "wC hR"(red below)
change text to "Attributed", and then you can set the font and size
for your text.
run to check if it works
Try this it will work
In my case when i try to set "Silversky Technology" as Attributed text for label from interface builder its not show when i run in simulator but its show in interface builder. So i used one trick i made Silversky font with 1 pixel bigger then Technology text.
Attribute text have issue with same size of font so change size of 1 word this thing work with me.
May be this is xcode bug but this trick work for me.
Met the same problem: the attribute font for UILabel in storyboard didn't work in run time. Using this UIFont+IBCustomFonts.m works for me
https://github.com/deni2s/IBCustomFonts
The same problem.
Solved: Just check Selectable in TextView. Without this i have standard System font.
Double click and install the font to the system. It will work (Xcode 8.2)
#Hamidptb solution works, make sure to get the correct name of the font (once you've added it to Font Book)
Open the Font Book application, navigate to your font then press Command+I. The PostScript name is the font name you want to use here:
UILabel.appearance().font = UIFont(name: "PostScriptName", size: 17)
I was trying to get tableView cells with text having multiple paragraphs. The attributed strings seemed to be a way to get extra space between the paragraphs (something a bit nicer looking than doing two line-feeds in the string). Came across this and other posts when I discovered that the IB settings didn't apply at run time when you wanted to put different text in the cell.
The main thing I came up with was adding an extension to String (using Swift) to
create an attributed string with certain characteristics. Example here uses the Marker Felt font, as it is easily distinguishable from Helvetica. The example also shows a little extra bit of spacing between paragraphs to make them more distinct from each other.
extension String {
func toMarkerFelt() -> NSAttributedString {
var style = NSMutableParagraphStyle()
style.paragraphSpacing = 5.0
let markerFontAttributes : [NSObject : AnyObject]? = [
NSFontAttributeName : UIFont(name: "Marker Felt", size: 14.0)!,
NSParagraphStyleAttributeName: style,
NSForegroundColorAttributeName : UIColor.blackColor()
]
let s = NSAttributedString(string: self, attributes: markerFontAttributes)
return s
}
}
Then, in my custom tableViewCell, you send it the text you want and it converts it to an attributed string on the UILabel.
// MarkerFeltCell.swift
class MarkerFeltCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
func configureCellWithString(inputString : String) {
myLabel.attributedText = inputString.toMarkerFelt()
}}
In the view controller with the tableView, you should register your cell in viewDidLoad() -- I used a nib, so something like:
let cellName = "MarkerFeltCell"
tableView.registerNib(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName)
To get the cell to figure out how tall it should be, make a prototype cell that is used to get size info, and is never added into the tableView. So, in your
view controller's variables:
var prototypeSummaryCell : MarkerFeltCell? = nil
Then in (probably override - depending on your view controller) heightForRowAtIndexPath:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// ...
if xib == "MarkerFeltCell" {
if prototypeCell == nil {
prototypeCell = tableView.dequeueReusableCellWithIdentifier(xib) as? MarkerFeltCell
}
let width : CGFloat = tableView.bounds.width
let height : CGFloat = prototypeCell!.bounds.height
prototypeCell?.bounds = CGRect(x: 0, y: 0, width: width, height: height)
configureCell(prototypeCell!, atIndexPath: indexPath)
prototypeSummaryCell?.layoutIfNeeded()
let size = prototypeSummaryCell!.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
let nextHeight : CGFloat = ceil(size.height + 1.0)
return nextHeight
} else { // ...
In the above code, the prototypeCell will be filled in the first time it is needed. The prototypeCell is then used to figure out the height for the cell after going through the autosizing process. You will need to round up the height with the ceil() function. I also added in some extra fudge factor.
The final code bit is how you configure the text for the cell. For this example, simply:
func configureCell(cell :UITableViewCell, atIndexPath indexPath: NSIndexPath) {
if let realCell = cell as? MarkerFeltCell {
realCell.configureCellWithString("Multi-line string.\nLine 2.\nLine 3.") // Use \n to separate lines
}
}
Also, here is a shot of the nib. Pinned the label to the edges of the cell (with margin desired), but used a "Greater Than or Equal" constraint, with a less than "Required" priority for the bottom constraint.
Set the label's font to Attributed. Actual IB font didn't matter.
The result in this case:
In case of attributed string you can add custom font in font list as -
Click on font icon this will display following dialog .In the following dialog you can add your own category or existing one for custom font.attributed font dialog
After it click on Manage Fonts it open the following dialog select category in you created or existing one . Click on + sign to add font in the category.
Manage font dialog
that's have a simple and quick solition and that's work in my case .
that solution is add a code line in didFinishLaunchingWithOptions func in AppDelegate.swift file :
for textViews :
UITextView.appearance().font = UIFont(name: "IranSans", size: 17)
for labels :
UILabel.appearance().font = UIFont(name: "IranSans", size: 17)
and for rest of UiView like this two ☝️
For anyone applying custom fonts to attributed string in code: Try setting it in viewDidLayoutSubviews. My mistake was doing it in viewDidLoad, it won't be applied there.

Resources