Swiftでシステムデフォルト以外のフォントを使いたい時が出て来ると思います。
物によっては.ttfなどをダウンロードしてプロジェクトファイルに入れるというのもありだと思います。
自分も使いたい状況が出て来たのですが、調べたところobj-cのものが多くSwiftでのやり方が少なかったので備忘録で残しておきます。
裏側がどうなってるのか詳しく理解していないので、FontどのFontが落とせるのか正確にわかっていません。(多分、Macにデフォルトで入ってたりFontBookで見れるの落とせるんじゃねーのか認識です)
Fontダウンロードついでに、GCD(Grand Central Dispatch)というのを使って非同期で処理を行わせています。
また、iPhoneに一度インストールしておくと二度目からは必要ないそうです。
fontDownload.swift
import Foundation
import UIKit
final class FontDownloadViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 非同期処理のグループ化変数
let group = DispatchGroup()
// 非同期処理(その1) medium font用
let mediumQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".medium", attributes: .concurrent)
// 非同期処理(その2) bold font用
let boldQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".bold", attributes: .concurrent)
mediumQueue.async(group: group) {
// 游ゴシックのMediumを指定
self.install(fontName: "YuGo-Medium")
}
boldQueue.async(group: group) {
// 游ゴシックのBoldを指定
self.install(fontName: "YuGo-Bold")
}
// 非同期処理 2つが終わると呼ばれる
group.notify(queue: DispatchQueue.main) {
// 遷移処理など?
}
}
func install(fontName: String) {
// 終わったかどうか
var result = false
// スレッドの排他処理
let semaphore = DispatchSemaphore(value: 0)
// 終わるまで待つ
while !result {
self.coreInstall(name: fontName, completion: {
result = $0
// semaphoreのvalueをインクリメントする
semaphore.signal()
})
// semaphoreのvalueが1以上の数であれば処理を終了、0か負の数であれば処理を待つ
semaphore.wait()
// 0.01秒処理を止める
Thread.sleep(forTimeInterval: 0.01)
}
}
func coreInstall(name: String, completion: @escaping (Bool) -> Void) {
// UIFontのインスタンスが出来ればnilではない(iPhoneにインストールされている)
if UIFont(name: name, size: 16.0) != nil {
completion(true)
}
let desc = UIFontDescriptor(name: name, size: 16.0)
CTFontDescriptorMatchFontDescriptorsWithProgressHandler( [desc] as CFArray, nil, { state, prog in
print(state)
switch state {
case .didBegin:
print("matching did begin: " + name)
case .stalled:
print("stalled")
case .didMatch:
print("match")
case .willBeginQuerying:
print("willBeginQuerying")
case .willBeginDownloading:
print("downloading will begin")
case .downloading:
let d = prog as NSDictionary
let key = kCTFontDescriptorMatchingPercentage
let cur = d[key]
if let cur = cur as? NSNumber {
print("progress: %@%%", cur)
}
print("downloading")
case .didFinishDownloading:
print("downloading did finish")
case .didFailWithError:
print("downloading failed")
case .didFinish:
print("matching did finish")
// 失敗しようがしまいが終わったらtrueを与えてメソッドをコール
completion(true)
default:break
}
return true
})
}
}
上記のはViewCotnrollerに書くパターンですが、UIFontを拡張して関数にしておくのもありだと思います。
ダウンロードが完了した次のViewからFontが適応されるかと思います。
何か間違いがあればすみません。教えていただけると助かります。