Font caching

This commit is contained in:
Ilya Laktyushin 2023-05-30 15:57:54 +04:00
parent 2b22d175d8
commit 9bd6dd35c7

View File

@ -8,6 +8,21 @@ public struct Font {
case monospace case monospace
case round case round
case camera case camera
var key: String {
switch self {
case .regular:
return "regular"
case .serif:
return "serif"
case .monospace:
return "monospace"
case .round:
return "round"
case .camera:
return "camera"
}
}
} }
public struct Traits: OptionSet { public struct Traits: OptionSet {
@ -58,9 +73,58 @@ public struct Font {
return .regular return .regular
} }
} }
var key: String {
switch self {
case .regular:
return "regular"
case .light:
return "light"
case .medium:
return "medium"
case .semibold:
return "semibold"
case .bold:
return "bold"
case .heavy:
return "heavy"
}
}
} }
private final class Cache {
private var lock: pthread_rwlock_t
private var fonts: [String: UIFont] = [:]
init() {
self.lock = pthread_rwlock_t()
let status = pthread_rwlock_init(&self.lock, nil)
assert(status == 0)
}
func get(_ key: String) -> UIFont? {
let font: UIFont?
pthread_rwlock_rdlock(&self.lock)
font = self.fonts[key]
pthread_rwlock_unlock(&self.lock)
return font
}
func set(_ font: UIFont, key: String) {
pthread_rwlock_wrlock(&self.lock)
self.fonts[key] = font
pthread_rwlock_unlock(&self.lock)
}
}
private static let cache = Cache()
public static func with(size: CGFloat, design: Design = .regular, weight: Weight = .regular, traits: Traits = []) -> UIFont { public static func with(size: CGFloat, design: Design = .regular, weight: Weight = .regular, traits: Traits = []) -> UIFont {
let key = "\(size)_\(design.key)_\(weight.key)_\(traits.rawValue)"
if let cachedFont = self.cache.get(key) {
return cachedFont
}
if #available(iOS 13.0, *), design != .camera { if #available(iOS 13.0, *), design != .camera {
let descriptor: UIFontDescriptor let descriptor: UIFontDescriptor
if #available(iOS 14.0, *) { if #available(iOS 14.0, *) {
@ -101,45 +165,51 @@ public struct Font {
} }
} }
let font: UIFont
if let updatedDescriptor = updatedDescriptor { if let updatedDescriptor = updatedDescriptor {
return UIFont(descriptor: updatedDescriptor, size: size) font = UIFont(descriptor: updatedDescriptor, size: size)
} else { } else {
return UIFont(descriptor: descriptor, size: size) font = UIFont(descriptor: descriptor, size: size)
} }
self.cache.set(font, key: key)
return font
} else { } else {
let font: UIFont
switch design { switch design {
case .regular: case .regular:
if traits.contains(.italic) { if traits.contains(.italic) {
if let descriptor = UIFont.systemFont(ofSize: size, weight: weight.weight).fontDescriptor.withSymbolicTraits([.traitItalic]) { if let descriptor = UIFont.systemFont(ofSize: size, weight: weight.weight).fontDescriptor.withSymbolicTraits([.traitItalic]) {
return UIFont(descriptor: descriptor, size: size) font = UIFont(descriptor: descriptor, size: size)
} else { } else {
return UIFont.italicSystemFont(ofSize: size) font = UIFont.italicSystemFont(ofSize: size)
} }
} else { } else {
return UIFont.systemFont(ofSize: size, weight: weight.weight) return UIFont.systemFont(ofSize: size, weight: weight.weight)
} }
case .serif: case .serif:
if weight.isBold && traits.contains(.italic) { if weight.isBold && traits.contains(.italic) {
return UIFont(name: "Georgia-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Georgia-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if weight.isBold { } else if weight.isBold {
return UIFont(name: "Georgia-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Georgia-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if traits.contains(.italic) { } else if traits.contains(.italic) {
return UIFont(name: "Georgia-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Georgia-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else { } else {
return UIFont(name: "Georgia", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Georgia", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} }
case .monospace: case .monospace:
if weight.isBold && traits.contains(.italic) { if weight.isBold && traits.contains(.italic) {
return UIFont(name: "Menlo-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Menlo-BoldItalic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if weight.isBold { } else if weight.isBold {
return UIFont(name: "Menlo-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Menlo-Bold", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else if traits.contains(.italic) { } else if traits.contains(.italic) {
return UIFont(name: "Menlo-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Menlo-Italic", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} else { } else {
return UIFont(name: "Menlo", size: size - 1.0) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: "Menlo", size: size - 1.0) ?? UIFont.systemFont(ofSize: size)
} }
case .round: case .round:
return UIFont(name: ".SFCompactRounded-Semibold", size: size) ?? UIFont.systemFont(ofSize: size) font = UIFont(name: ".SFCompactRounded-Semibold", size: size) ?? UIFont.systemFont(ofSize: size)
case .camera: case .camera:
func encodeText(string: String, key: Int16) -> String { func encodeText(string: String, key: Int16) -> String {
let nsString = string as NSString let nsString = string as NSString
@ -152,11 +222,15 @@ public struct Font {
return result as String return result as String
} }
if case .semibold = weight { if case .semibold = weight {
return UIFont(name: encodeText(string: "TGDbnfsb.Tfnjcpme", key: -1), size: size) ?? UIFont.systemFont(ofSize: size, weight: weight.weight) font = UIFont(name: encodeText(string: "TGDbnfsb.Tfnjcpme", key: -1), size: size) ?? UIFont.systemFont(ofSize: size, weight: weight.weight)
} else { } else {
return UIFont(name: encodeText(string: "TGDbnfsb.Sfhvmbs", key: -1), size: size) ?? UIFont.systemFont(ofSize: size, weight: weight.weight) font = UIFont(name: encodeText(string: "TGDbnfsb.Sfhvmbs", key: -1), size: size) ?? UIFont.systemFont(ofSize: size, weight: weight.weight)
} }
} }
self.cache.set(font, key: key)
return font
} }
} }