Font Descriptor는 iOS의 UIFontDescriptor와 macOS의 NSFontDescriptor 객체로, 모든 폰트 메타데이터(패밀리, 타입페이스, 크기, 자간, 행간, 타이포그래피 속성)를 캡슐화합니다. UIFont(name:size:)를 통해 직접 폰트를 생성하는 대신, 개발자는 디스크립터를 통해 원하는 특성을 설명하고 시스템이 사용 가능한 폰트 중 최적의 일치 항목을 선택합니다. Apple 개발자 문서에 따르면 UIFontDescriptor는 20개 이상의 폰트 속성을 지원하며 UIKit에서 Core Text, SwiftUI에 이르기까지 모든 iOS 타이포그래피 프레임워크에서 사용됩니다.
주요 포인트
Font Descriptor는 특정 폰트 파일 대신 키-값 쌍의 집합으로 폰트 특성을 설명하는 객체입니다. 이름으로 폰트를 생성하는 대신 개발자가 패밀리, 타입페이스, 크기 및 선택적 속성을 지정하면 시스템이 최적의 일치 항목을 선택합니다. 이 접근 방식은 적응형 타이포그래피에 중요합니다. 동일한 디스크립터가 사용 가능한 폰트 집합이 다른 여러 iOS 버전에서 작동할 수 있습니다. iOS 휴먼 인터페이스 가이드라인에 따르면 디스크립터를 통한 폰트 매칭이 동적 폰트 작업의 권장 방법입니다.
Font Descriptor가 없으면 개발자는 각 폰트의 가용성을 이름으로 확인하고 수동으로 폴백 옵션을 정의해야 합니다. 디스크립터는 이 프로세스를 자동화합니다. 타입페이스의 호환성 매트릭스(일반, 굵게, 이탤릭, 굵은 이탤릭)를 포함하며 정확한 일치 항목이 없을 경우 가장 가까운 사용 가능한 항목을 반환합니다. 시스템 매칭은 Core Text 수준에서 작동하며 통합된 NSFontDescriptor API를 통해 iOS와 macOS 모두에서 사용할 수 있습니다.
디스크립터는 사용자 정의 폰트(Info.plist를 통해 로드되거나 수동으로 추가된)에도 사용됩니다. 폰트가 시스템에 등록된 경우 해당 디스크립터는 시스템 폰트와 완전히 동일한 방식으로 생성됩니다. 크로스 플랫폼 참고: 디스크립터의 개념은 Android에서 Typeface.Builder를 통해서도 존재하지만 UIFontDescriptor를 통한 iOS 구현은 Android의 5개와 비교하여 20개 이상의 훨씬 더 많은 속성을 제공합니다.
UIFontDescriptor는 [UIFontDescriptor.AttributeName: Any] 유형의 fontAttributes 사전에 그룹화된 속성을 포함합니다. 주요 속성에는 폰트 패밀리, 이름, 크기, 특성, 변환 매트릭스, 광학 크기 및 텍스트 스타일이 포함됩니다. Apple WWDC 2019 세션 227에 따르면 텍스트 스타일(Dynamic Type) 사용은 접근성 호환 애플리케이션에 필수입니다.
traits 속성은 기호 특성을 포함하는 사전입니다: 두께(UIFontWeightTrait), 기울기(UIFontSlantTrait), 너비(UIFontWidthTrait) 등. 이 값들은 폰트 매처가 타입페이스를 선택하는 데 사용합니다. 예를 들어 UIFontWeightTrait 값 0.0은 일반 타입페이스, 0.3은 굵게를 의미합니다. 시스템은 숫자 값을 사용 가능한 폰트 변형에 자동으로 매핑합니다.
추가 속성에는 OpenType 기능을 위한 featureSettings(합자, 커닝, 숫자 비율), 사용자 표시용 visibleName, 폰트의 아핀 변환을 위한 matrix, 캐스케이딩 폴백 폰트 목록을 위한 cascadeList가 포함됩니다. 전체 속성 목록은 iOS 및 macOS의 사실상 모든 타이포그래피 시나리오를 다룹니다.
| 속성 | 유형 | 설명 |
|---|---|---|
| UIFontDescriptorFamily | String | 폰트 패밀리 (SF Pro, Helvetica Neue) |
| UIFontDescriptorName | String | 전체 폰트 이름 (HelveticaNeue-Bold) |
| UIFontDescriptorSize | NSNumber | 포인트 단위 폰트 크기 |
| UIFontDescriptorMatrix | NSValue | 아핀 변환 매트릭스 |
| UIFontDescriptorTraits | [String: Any] | 타입페이스의 기호 특성 |
| UIFontDescriptorTextStyle | String | Dynamic Type 스타일 (headline, body, caption) |
iOS에서 Font Descriptor로 작업하는 기본 방법은 디스크립터를 생성한 다음 UIFont를 생성하는 것입니다. 이는 정확한 이름을 모르면서 특정 특성을 가진 폰트가 필요할 때 선호되는 접근 방식입니다. 프로세스는 두 단계로 구성됩니다: 이니셜라이저를 통해 UIFontDescriptor를 생성하고 디스크립터에서 font(withSize:) 메서드를 호출합니다.
import UIKit
// 패밀리용 폰트 디스크립터 생성
let descriptor = UIFontDescriptor.init(
fontAttributes: [
.family: "SF Pro",
.traits: [
.weight: UIFont.Weight.bold
]
]
)
// 디스크립터에서 UIFont 생성
let font = descriptor.font(withSize: 17)
이 접근 방식은 시스템이 현재 기기에서 SF Pro의 굵은 타입페이스에 가장 적합한 일치 항목을 선택하도록 보장합니다. 주요 장점 — 코드가 iOS 버전에 의존하지 않습니다: iOS 13에서는 SF Pro가 사용되고 이전 버전에서는 Helvetica Neue 또는 기본 시스템 폰트가 사용됩니다.
동적 크기의 폰트를 만들려면 텍스트 스타일과 스케일을 사용하여 이니셜라이저를 사용합니다. Dynamic Type은 사용자 설정에 따라 자동으로 크기를 조정합니다:
// Dynamic Type 디스크립터
let bodyDescriptor = UIFontDescriptor
.preferredFontDescriptor(withTextStyle: .body)
// 폰트 굵게 만들기
let boldDescriptor = bodyDescriptor.withSymbolicTraits([.traitBold])
// 동적 크기를 유지하면서 UIFont 생성
let boldBodyFont = UIFont(
descriptor: boldDescriptor!,
size: bodyDescriptor.pointSize
)
중요: withSymbolicTraits 메서드는 옵셔널 디스크립터를 반환합니다 — 요청한 타입페이스를 사용할 수 없으면 nil을 반환합니다. 사용 전에 항상 옵셔널을 확인하세요.
Font Descriptor는 불변 모델을 지원합니다 — 각 수정 메서드는 변경된 속성과 함께 새 디스크립터를 반환합니다. 이는 값 의미론을 따르며 코드를 예측 가능하게 만듭니다: 원래 디스크립터는 절대 변경되지 않습니다. 주요 수정 메서드는: 속성 추가를 위한 addingAttributes, 타입페이스 변경을 위한 withFace, 크기 변경을 위한 withSize, 아핀 변환을 위한 withMatrix입니다.
addingAttributes 메서드는 가장 유연합니다. 속성 사전을 받아 새 디스크립터를 반환합니다. 키가 일치하면 새 값이 이전 값을 대체합니다. 이를 통해 단일 호출로 두께 추가, 크기 변경 및 합자 활성화를 결합할 수 있습니다. Apple 기술 Q&A QA1682에 따르면 addingAttributes는 확장을 통한 대량 수정의 권장 방법입니다.
실용적 사용 사례: 코드 편집기에서 고정폭 텍스트용 폰트 만들기. 개발자는 시스템 고정폭 디스크립터를 가져와 가는 선을 위해 줄어든 두께를 추가하고 featureSettings를 통해 자간을 늘립니다. 이렇게 하면 별도의 폰트를 로드하지 않고도 더 읽기 쉬운 코드를 제공합니다.
let monoDescriptor = UIFontDescriptor.monospacedDigitFontDescriptor()
// 사용자 정의 속성 추가
let customDescriptor = monoDescriptor.addingAttributes([
.size: 14,
.traits: [
.weight: UIFont.Weight.medium
]
])
let monoFont = UIFont(
descriptor: customDescriptor,
size: 14
)
제한 사항: 디스크립터 생성 후 모든 속성을 변경할 수 있는 것은 아닙니다. 예를 들어 폰트 패밀리는 초기화 시 한 번 설정됩니다. 패밀리를 변경해야 하는 경우 새 디스크립터를 만드세요. 이는 디스크립터가 특정 폰트 리소스에 연결되는 Core Text 아키텍처와 관련이 있습니다.
폰트 매칭은 Font Descriptor의 핵심 기능으로 설치된 폰트 중에서 요청된 속성에 가장 적합한 것을 자동으로 선택합니다. 프로세스는 세 단계로 구성됩니다: 필터링(호환되지 않는 폰트 제외), 순위 지정(일치 정도에 따른 정렬), 선택(최상의 결과 반환). 매칭은 모든 iOS 타이포그래피의 기반이 되는 프레임워크인 Core Text 수준에서 수행됩니다.
디스크립터와 일치하는 모든 폰트를 찾으려면 matchingFontDescriptors 메서드를 사용합니다. 관련성에 따라 정렬된 디스크립터 배열을 반환합니다. 개발자는 정확한 일치(첫 번째 요소)를 선택하거나 사용자 지정 로직을 위해 옵션을 반복할 수 있습니다. Apple 폰트 처리 가이드에 따르면 이름으로 열거하지 않고 특정 패밀리에 사용 가능한 타입페이스 목록을 가져오는 유일한 방법입니다.
사용 사례: 텍스트 설정 인터페이스에서 폰트를 선택할 때. 사용자가 패밀리를 선택하면 애플리케이션이 matchingFontDescriptors를 사용하여 사용 가능한 모든 타입페이스를 가져와 피커에 표시합니다. 매칭이 없으면 개발자는 iOS 버전 간에 변경되는 옵션 목록을 하드코딩해야 합니다.
let searchDescriptor = UIFontDescriptor.init(
fontAttributes: [.family: "SF Mono"]
)
// 사용 가능한 모든 타입페이스 가져오기
let matchingDescriptors = searchDescriptor
.matchingFontDescriptors(withMandatoryKeys: nil)
for descriptor in matchingDescriptors {
print(descriptor.object(forKey: .visibleName) ?? "unnamed")
}
필수 키 — 검색을 제한하는 매개변수입니다. [.family]를 지정하면 매칭이 지정된 패밀리의 모든 폰트를 반환합니다. [.family, .traits]를 지정하면 패밀리와 타입페이스가 모두 일치하는 것만 반환합니다. 필수 키를 사용하면 폰트 하위 집합을 검색할 때 매칭 속도가 빨라집니다.
성능: 매칭은 동기 작업으로 iOS에서 0.5~5ms가 소요됩니다. 단일 검색의 경우 무시할 수 있지만 디스크립터를 대량 반복하는 경우(예: 50개 이상의 폰트가 있는 피커) 결과를 캐시하는 것이 좋습니다. Apple은 화면 로드 시 한 번 매칭을 수행하고 디스크립터 배열을 저장할 것을 권장합니다.
Core Text는 iOS 및 macOS에서 타이포그래피 작업을 위한 저수준 C 기반 프레임워크입니다. UIFontDescriptor의 대응물은 CTFontDescriptor로 Core Foundation 수준에서 동일한 기능을 제공합니다. CTFontDescriptor는 Core Text API에서 폰트 생성, 텍스트 측정 및 복잡한 타이포그래피 렌더링에 사용됩니다. UIFontDescriptor는 본질적으로 확장된 속성 집합을 가진 CTFontDescriptor의 Objective-C 래퍼입니다.
UIFontDescriptor와 CTFontDescriptor 간의 톨프리 브리징을 통해 변환 없이 UIKit과 Core Text 간에 디스크립터를 전달할 수 있습니다. 이는 사용자 지정 텍스트 렌더링을 사용하는 애플리케이션(예: TextKit 기반 텍스트 편집기 또는 구문 강조가 있는 코드 편집기)에 중요합니다. 예: CTFontDescriptorCreateCopyWithAttribute는 속성이 추가된 디스크립터의 복사본을 만듭니다 — UIFontDescriptor의 addingAttributes와 유사합니다.
CTFontDescriptor의 주요 속성 kCTFontURLAttribute는 폰트 파일의 URL을 포함합니다. 이를 통해 폰트가 시스템 폰트인지 사용자 정의 폰트인지 확인할 수 있습니다. 사용자 정의 폰트의 경우 URL은 폰트 파일(.ttf 또는 .otf)을 가리키고 시스템 폰트의 경우 시스템 폰트 디렉토리를 가리킵니다. 디스크립터를 통한 데이터는 읽기 전용입니다. URL을 통한 폰트 파일 수정은 지원되지 않습니다.
고급 타이포그래피(사용자 정의 합자, 문맥 대체, 대체 글리프)를 위해서는 kCTFontFeatureSettingsAttribute 키와 함께 CTFontDescriptorCopyAttribute를 사용합니다. 이 속성에는 디스크립터를 통해 추가할 수 있는 OpenType 기능 사전의 배열이 포함됩니다. Apple Core Text 프로그래밍 가이드에 따르면 UIFontDescriptor와 Core Text를 결합하면 애플리케이션 타이포그래피를 완전히 제어할 수 있습니다.
자주 묻는 질문
UIFont는 텍스트를 렌더링하는 즉시 사용 가능한 폰트입니다. Font Descriptor는 폰트 설명 — UIFont를 생성할 수 있는 속성 집합입니다. 하나의 디스크립터로 다양한 크기의 여러 UIFont를 생성할 수 있습니다.
네. 폰트가 시스템에 등록된 경우(Info.plist 또는 Core Graphics를 통해) 해당 디스크립터는 시스템 폰트와 마찬가지로 패밀리 또는 이름으로 생성됩니다. 매칭은 등록된 모든 폰트에서 작동합니다.
표시 이름을 얻으려면 descriptor.object(forKey: .visibleName)를, PostScript 이름을 얻으려면 descriptor.postscriptName을 사용하세요. PostScript 이름은 UIFont(name:size:)를 통해 폰트를 만들 때 사용됩니다.
캐스케이드 목록은 폴백 폰트 역할을 하는 중첩된 디스크립터의 배열입니다. 기본 폰트에 필요한 문자가 없으면 시스템이 캐스케이드 목록에서 순차적으로 폰트를 확인합니다. 이모지 및 CJK 문자 지원에 사용됩니다.
네. SwiftUI는 Font(descriptor:size:)를 통해 Font Descriptor를 지원합니다. SwiftUI 래퍼가 UIFontDescriptor를 받아 모든 디스크립터 속성(커닝, 합자, 타입페이스)을 유지하면서 SwiftUI 폰트를 만듭니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.