Downloading and installing a custom font in iOS [duplicate] - ios

This question already has answers here:
Download font .ttf file from web and store on iPhone
(5 answers)
Closed 7 years ago.
I have searched here for the answer to this but I haven't found any posts that I can use to get an answer. I have only found answers about pre-installing the font to the plist of the app which won't work for me.
What I am trying to do is download a Font from a URL, store the file locally to the app and then install or use that font in controls.
I am at the point of installing the font to the tablet to use. I can add it to CTFontManager without any issues using the code below.
private func InstallFont(dest:String)
{
let fontData = NSData(contentsOfFile: dest)!
let dataProvider = CGDataProviderCreateWithCFData(fontData)
let cgFont = CGFontCreateWithDataProvider(dataProvider)!
var error: Unmanaged<CFError>?
if !CTFontManagerRegisterGraphicsFont(cgFont, &error)
{
print("Error loading Font!")
}
}
The issue that I am facing is that I cannot seem to find the font I have installed anywhere in the app, I have tried to set it like below but just goes to default which tells me it can not be found.
self.font = UIFont(name: "Font Name", size: 20.0)
I have also looped through the font family's to see if it has been installed and no success there either.
for x in UIFont.familyNames()
{
print(x)
for z in UIFont.fontNamesForFamilyName(x)
{
print("== \(z)")
}
}
Is what I am trying to achieve even possible? If so what am I doing wrong?

It is possible. I created an example project in github. You have to just add the line below.
if !CTFontManagerRegisterGraphicsFont(cgFont, &error)
{
print("Error loading Font!")
}
else
{
let fontName = CGFontCopyPostScriptName(cgFont)
uiFont = UIFont(name: String(fontName) , size: 30)
}
Thank you for Michael Dautermann, he was right.
Github project link

According to the answers in this related question, you should be able to download your font and register it the way you are already doing (which it sounds like you are doing correctly), but you simply need to get the Postscript name and use that to create a corresponding UIFont:
var uiFont : UIFont?
let fontData = NSData(contentsOfFile: dest)!
let dataProvider = CGDataProviderCreateWithCFData(fontData)
let cgFont = CGFontCreateWithDataProvider(dataProvider)!
var error: Unmanaged<CFError>?
if !CTFontManagerRegisterGraphicsFont(cgFont, &error)
{
print("Error loading Font!")
} else {
let fontName = CGFontCopyPostScriptName(fontRef)
uiFont = UIFont(name: fontName, size: 30)
}

Related

Set Custom Font To UILabel in iOS Swift

Here, I want to set custom font(getting "Font Name" from backend) to UILabel, without adding font files in xCode project.
Please help if find any solution.
Currently I am trying with "Inkwell" in iOS. Here is the link -> https://github.com/ninjaprox/Inkwell
steps which I have followed:
Installed pod 'Inkwell', '~> 1.2'
Added Google API Key in AppDelegate like, Inkwell.shared.APIKey =
"Api key here"
import "Inkwell"
In viewWillAppear, updating label font:
let font = Font(family: "Droid Sans", variant: .regular)
fontOperation = Inkwell.shared.font(for: font, size: fontSize) { uifont in
print("Font name -> \(String(describing: uifont))")
self.lblTestFont.font = uifont ?? self.defaultFont
self.lblTestFont.text = "text"
}
But getting "Font name" nil. Font Operation failed.
How I can fix this issue?
To use custom font in iOS app you need to add those font in info.plist file and also add those font in project.
Once you do all these step then you can use the following code to check that our custom font is available to use or not.
//it will list all the fonts available
for fontFamily in UIFont.familyNames {
print(UIFont.fontNames(forFamilyName: fontFamily))
}

List of installed fonts

I am working on an application where the user can install different custom fonts. The application presents the user a table list of fonts he can install. (3 random fonts provided for this example)
The fonts are downloaded when the user opens the app, and the fonts are dynamically loaded with this function. The custom fonts are not added in the info.plist.
func install_font(font_path : String) -> Bool
{
let font_data = try! Data(contentsOf: URL(fileURLWithPath: font_path))
if let provider = CGDataProvider.init(data: font_data as CFData)
{
var error: Unmanaged<CFError>?
let font:CGFont = CGFont(provider)!
if (!CTFontManagerRegisterGraphicsFont(font, &error))
{
print(error.debugDescription)
return false
}
else
{
return true
}
}
return false
}
Until now I was using this function, but this displays all fonts: installed ones and the dynamically loaded.
func show_all_fonts()
{
UIFont.familyNames.forEach({ familyName in
let fontNames = UIFont.fontNames(forFamilyName: familyName)
print(familyName, fontNames)
})
}
Is there any way to display only the installed fonts? I need a method to differentiate the installed fonts vs the fonts dynamically loaded, so I can put a checkmark on table cell for the installed ones.
Why don't you fetch and persist the list of installed fonts on initial launch of your app after installation. That way you can basically just run a union on the persisted font list with the after-first-launch list of fonts.

How to localize iMessage stickers?

I am creating an iMessage Sticker Pack Extension that have stickers that contain texts. I don't see any option in Xcode to localize the stickers? Is it possible to localize stickers?
Maybe it's obvious, but you can manage it inside of an extension. If you create not a pack, but extension, you can fetch exact stickers for your localization
If you want to show different sticker-assets dependant on e.g. your device's language, you need to create a iMessage Application instead of a Sticker Pack Application.
And you need to write some code as it's not possible to have this behaviour within a simple Sticker Pack Application.
However this is pretty simple. For a start, follow this tutorial:
http://blog.qbits.eu/ios-10-stickers-application/
Some code-syntax there is outdated, but XCode will help you to easily fix that.
You can place your localized resources inside different folders and drag'n'drop them into you XCode project (make sure to check "Create folder references"):
Screenshot of project structure
You can then do something like this in your viewDidLoad():
//-- get current language set
var imageSetPath = "/EN"; //-- default to english
let languageCode = NSLocale.preferredLanguages[0]
if languageCode.hasPrefix("zh-Hans") { imageSetPath = "/CNS" }
else if languageCode.hasPrefix("zh-Hans") { imageSetPath = "/CNT" }
else if languageCode.hasPrefix("ko") { imageSetPath = "/KR" }
else if languageCode.hasPrefix("ja") { imageSetPath = "/JP" }
//-- load localized stickers
imageUrls = recursivePathsForResources(path: Bundle.main.bundlePath + imageSetPath, type: "png")
loadStickers(urls: imageUrls)
where
func recursivePathsForResources(path: String, type: String) -> [URL] {
// Enumerators are recursive
let enumerator = FileManager.default.enumerator(atPath: path)
var filePaths = [URL]()
while let filePath = enumerator?.nextObject() as? String {
if NSURL(fileURLWithPath: filePath).pathExtension == type {
let url = URL(fileURLWithPath: path).appendingPathComponent(filePath)
filePaths.append(url)
}
}
return filePaths
}
(altered from https://stackoverflow.com/a/5860015/6649403)

Getting Climacons to display in UILabel with CZWeatherKit in Swift

So I am using the CZWeatherKit library to grab weather data from forecast.io.
When I get results, it sends a climacon UInt8 char, which should match to an icon if the climacon font is installed. I did that but it only shows the char, not the actual icon. Here is the code, it prints a quote i.e. " which is the correct mapping to ClimaconCloudSun, but the icon doesn't show. I followed these instructions to install the climacons.ttf font
request.sendWithCompletion { (data, error) -> Void in
if let error = error {
print(error)
} else if let weather = data {
let forecast = weather.dailyForecasts.first as! CZWeatherForecastCondition
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// I get back good results, this part works
let avgTempFloat = (forecast.highTemperature.f + forecast.lowTemperature.f) / 2
let avgTemp = NSDecimalNumber(float: avgTempFloat).decimalNumberByRoundingAccordingToBehavior(rounder)
self.temperatureLabel.text = String(avgTemp)
self.weatherLabel.text = forecast.summary
// this part does not work, it has the right char, but does not display icon
// I tried setting self.climaconLabel.font = UIFont(name: "Climacons-Font", size: 30) both in IB and programmatically
let climaChar = forecast.climacon.rawValue
let climaString = NSString(format: "%c", climaChar)
self.climaconLabel.text = String(climaString)
})
}
}
I solved the exact same issue, the problem was the font file. Replace your current font with the one provided here: https://github.com/comyar/Sol/blob/master/Sol/Sol/Resources/Fonts/Climacons.ttf
You've probably moved on from this problem by now, but I'll leave this here for future use.
You need to call setNeedsLayout on the label after you change the title text to the desired value, and the label will change to the corresponding icon.

swift alternative for fontDescriptorWithSymbolicTraits ios 8 bug

I'm trying to create a Swift custom text editor (lite version) and got stuck on iOS 8 fontDescriptorWithSymbolicTraits bug, returning nil. Are there any Swift workarounds?
I've created a small project sample here , you can download and run the following scenario:
Select a text
Click on "B" button
Click on "I" button
The app crashes, fontDescriptorWithSymbolicTraits returns nil :(
private func addOrRemoveTraitWithName(traitName: String, traitValue: UInt32) {
let range = self.textEditor.selectedRange
let currentAttributesDict = self.textEditor.textStorage.attributesAtIndex(range.location, effectiveRange: nil)
let currentFont = currentAttributesDict[NSFontAttributeName]
let fontDescriptor = currentFont?.fontDescriptor()
let fontNameAttribute = fontDescriptor?.fontAttributes()[UIFontDescriptorNameAttribute]
var changedFontDescriptor: UIFontDescriptor?
if fontNameAttribute?.rangeOfString(traitName).location == NSNotFound {
let existingTraitsWithNewTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor!.symbolicTraits.rawValue | traitValue)
changedFontDescriptor = fontDescriptor?.fontDescriptorWithSymbolicTraits(existingTraitsWithNewTrait)
} else {
let existingTraitsWithoutTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor!.symbolicTraits.rawValue & ~traitValue)
changedFontDescriptor = fontDescriptor?.fontDescriptorWithSymbolicTraits(existingTraitsWithoutTrait)
}
let updatedFont = UIFont(descriptor: changedFontDescriptor!, size: 0.0)
var dict = [String : AnyObject]()
dict[NSFontAttributeName] = updatedFont
self.textEditor.textStorage.beginEditing()
self.textEditor.textStorage.setAttributes(dict, range: range)
self.textEditor.textStorage.endEditing()
}
I highly recommend to take a quick look at sample project
What options do I have?
I also seen that on Apple documentation the method that was used by other is missing from the SDK & documentation
What others say about this:
Font Descriptor returns nil in iOS 8
http://www.openradar.me/19922049
You're right about this. The bug was fixed in iOS 8.3, fortunately. The only reliable workaround is to drop down to the level of Core Text and perform the trait application there. It's a pain, but it solves the problem.
Thus, for instance, before iOS 8.3, this doesn't work:
if let body = UIFont(name: "GillSans", size: 15),
emphasis = body.fontDescriptor().fontDescriptorWithSymbolicTraits(.TraitItalic) {
fbody = body
femphasis = UIFont(descriptor: emphasis, size: 0)
}
So you have to import CoreText and drop down to that level:
if let body = UIFont(name: "GillSans", size: 15),
result = CTFontCreateCopyWithSymbolicTraits(body as CTFont, 0, nil, .ItalicTrait, .ItalicTrait) {
fbody = body
femphasis = result as UIFont
}
Fortunately, Swift 1.2 and later is aware that UIFont and CTFont are now toll-free bridged. Before Swift 1.2, this was even more complicated! And of course in earlier systems, they were not toll-free bridged and this was still more difficult.

Resources