Custom Font — to plik czcionki (TTF, OTF, WOFF2), który programista dodaje do aplikacji mobilnej w celu zastąpienia domyślnej czcionki systemowej. Niestandardowe czcionki są używane do tworzenia unikalnego stylu wizualnego, zgodności z brandbookiem i poprawy czytelności w różnych językach. Według danych Apple Fonts, niestandardowe czcionki są obsługiwane na wszystkich platformach Apple, a Android udostępnia mechanizm Font Family w celu uproszczenia ładowania. Prawidłowe podłączenie czcionki ma krytyczny wpływ na typografię i ogólny odbiór aplikacji.
Najważniejsze
Custom Font (niestandardowa czcionka) — to plik z krojem, który nie wchodzi w skład standardowego zestawu czcionek systemu operacyjnego. Programiści dodają takie czcionki do projektu w celu uzyskania unikalnej typografii zgodnej z marką. Niestandardowe czcionki mogą być zarówno płatnymi komercyjnymi krojami, jak i darmowymi czcionkami open-source, na przykład Inter, Montserrat, Roboto (wersja niestandardowa).
Typowe scenariusze użycia niestandardowych czcionek: logotypy i nagłówki aplikacji brandingowych, aplikacje do czytania ze szczególnymi wymaganiami typograficznymi, gry z tematycznymi czcionkami, aplikacje dla języków niełacińskich, gdzie czcionka systemowa nie obsługuje potrzebnych glifów. Według danych Google Fonts, ponad 60% aplikacji w top-100 dostosowuje czcionki w celu wizualnej dyferencjacji.
Systemowe czcionki (San Francisco w iOS, Roboto w Android) są zoptymalizowane pod interfejsy, ale nie nadają się do typografii brandingowej. Niestandardowa czcionka wzmacnia rozpoznawalność marki, może zawierać specjalne symbole (czcionki ikon) i poprawia czytelność dla określonych grup odbiorców. Jednak nadmierne użycie różnych czcionek pogarsza UX — zaleca się nie więcej niż 2-3 rodziny na aplikację.
Platformy mobilne obsługują kilka formatów czcionek. TrueType (TTF) — najbardziej rozpowszechniony format, kompatybilny ze wszystkimi wersjami iOS i Android. OpenType (OTF) — rozszerzenie TTF z obsługą ligatur, alternatywnych glifów i funkcji OpenType. Oba formaty mają rozszerzenia .ttf i .otf i działają identycznie na platformach mobilnych.
WOFF2 (Web Open Font Format 2) — skompresowany format dla sieci, obsługiwany w Android od wersji 10. WOFF2 zapewnia kompresję do 30-50% względem TTF. Dla iOS WOFF2 jest obsługiwany przez Safari, ale w aplikacjach natywnych wymagana jest konwersja do TTF/OTF. Variable Fonts (czcionki zmienne) — nowoczesny format przechowujący wszystkie kroje w jednym pliku.
| Format | iOS | Android | Kompresja | Cechy |
|---|---|---|---|---|
| TTF | Tak | Tak | Nie | Standardowy format, szeroka kompatybilność |
| OTF | Tak | Tak | Nie | Funkcje OpenType, ligatury, alternatywne glify |
| WOFF2 | Przez Safari | API 29+ | 30-50% | Skompresowany, do oszczędzania rozmiaru APK |
| Variable | iOS 11+ | API 29+ | Wysoka | Jeden plik dla wszystkich krojów |
Do programowania mobilnego zaleca się używanie OTF jako głównego formatu — obsługuje funkcje OpenType przy tym samym rozmiarze co TTF. Aby zaoszczędzić miejsce w APK, używaj kompresji przez woff2 lub ładowanie czcionek na żądanie.
Proces podłączania niestandardowej czcionki w iOS składa się z dwóch kroków: dodanie pliku do bundle projektu i rejestracja w Info.plist. Plik czcionki umieszczany jest w katalogu projektu (zwykle Resources/Fonts) i włączany do targetu. W Info.plist dodaje się tablicę UIAppFonts (Fonts provided by application) z nazwami plików czcionek, wraz z rozszerzeniem.
Po rejestracji czcionka jest dostępna przez UIFont(name:size:) pod nazwą PostScript. Nazwę PostScript można sprawdzić przez UIFont.familyNames i UIFont.fontNames(forFamilyName:). Jeśli nazwa jest nieprawidłowa, UIFont(name:size:) zwraca nil. Do debugowania zaleca się wyświetlenie wszystkich zarejestrowanych czcionek na ekranie przy pierwszym uruchomieniu.
Rozpatrzmy pełny cykl podłączania czcionki Montserrat w projekcie iOS w Swift. Plik Montserrat-Regular.ttf jest dodawany do projektu, wskazywany w Info.plist, następnie ładowany przez UIFont i stosowany do UILabel. Dla każdego kroju (regular, bold, italic) dodawany jest osobny plik i osobny wpis w Info.plist.
// 1. W Info.plist: UIAppFonts → "Montserrat-Regular.ttf"
// 2. Załaduj czcionkę w kodzie
guard let customFont = UIFont(name: "Montserrat-Regular", size: 16) else {
// fallback do czcionki systemowej
label.font = UIFont.systemFont(ofSize: 16)
return
}
label.font = customFont
// 3. Użyj w NSAttributedString
let attributes = [NSAttributedString.Key.font: customFont]
let attributed = NSAttributedString(string: "Text", attributes: attributes)
Obsługa sytuacji, gdy czcionka nie została załadowana (UIFont zwraca nil) — obowiązkowa. Zamiast awarii aplikacji użyj systemowej czcionki jako fallback. Jest to szczególnie ważne dla czcionek ładowanych z sieci lub dodawanych do opcjonalnych modułów.
Do czcionek pobieranych na żądanie używany jest CTFontManagerRegisterGraphicsFont. Ta metoda rejestruje czcionkę z danych w pamięci (Data) i udostępnia ją dla UIFont. Po użyciu czcionkę można wyrejestrować przez CTFontManagerUnregisterGraphicsFont. Dynamiczne ładowanie jest przydatne dla aplikacji z dużym zestawem czcionek, gdzie nie wszystkie są potrzebne od razu.
W Android niestandardowe czcionki dodawane są przez system zasobów res/font. Pliki TTF lub OTF umieszczane są w res/font/, po czym tworzone są zasoby XML Font Family do grupowania krojów. Począwszy od Android 8.0 (API 26) jest to jedyny zalecany sposób. Dla starszych wersji używane są assets i Typeface.createFromAsset.
XML Font Family opisuje dopasowanie między krojem (regular, bold, italic) a plikiem czcionki. W znacznikach XML atrybut android:fontFamily="@font/my_font" automatycznie wybiera odpowiedni plik przy setTypeface z odpowiednim stylem. Upraszcza to typografię — wystarczy wskazać rodzinę w temacie, a Android sam dobierze bold przy wywołaniu setTypeface(textView, Typeface.BOLD).
Utwórz plik XML w res/font/my_font.xml, w którym wymienione są wszystkie pliki czcionki dla różnych krojów. Każdy element ma atrybuty fontStyle (normal/italic) i fontWeight (100-900). Następnie w znacznikach wskaż android:fontFamily="@font/my_font". Przy użyciu textStyle="bold" Android automatycznie wybierze plik z fontWeight 700.
<!-- res/font/my_font.xml -->
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
<font
android:fontStyle="normal"
android:fontWeight="400"
app:font="@font/my_font_regular" />
<font
android:fontStyle="normal"
android:fontWeight="700"
app:font="@font/my_font_bold" />
</font-family>
Przy użyciu Font Family sposób zasobowy automatycznie buforuje czcionki i wybiera prawidłowy krój. Jeśli potrzebna waga jest nieobecna w rodzinie, Android syntetyzuje ją z najbliższej dostępnej. Dla precyzyjnej kontroli nad czcionką używaj Typeface.create() z jawnym wskazaniem pliku.
W frameworkach wieloplatformowych podłączanie czcionek jest zunifikowane. Flutter używa pubspec.yaml do wskazywania czcionek: pliki umieszczane są w folderze fonts/ projektu, następnie deklarowane w sekcji fonts z podaniem rodziny i krojów. Potem czcionka jest stosowana przez TextStyle(fontFamily: 'Montserrat') lub w temacie aplikacji. Flutter obsługuje TTF i OTF.
React Native używa dwóch podejść: natywnego (przez Info.plist i res/font) lub przez biblioteki react-native-vector-icons i @expo-google-fonts. Expo upraszcza proces — czcionki są ładowane przez expo-font: Font.loadAsync({ 'Montserrat': require('./assets/fonts/Montserrat.ttf') }). React Navigation i tematy pozwalają ustawić czcionkę globalnie.
Deklaracja czcionek w pubspec.yaml i zastosowanie w temacie MaterialApp. Pliki Montserrat-Regular.ttf i Montserrat-Bold.ttf umieszczane są w fonts/. Po deklaracji czcionka jest dostępna przez TextStyle w całej aplikacji. Flutter automatycznie używa pliku bold przy fontWeight: FontWeight.bold.
// pubspec.yaml
flutter:
fonts:
- family: Montserrat
fonts:
- asset: fonts/Montserrat-Regular.ttf
- asset: fonts/Montserrat-Bold.ttf
weight: 700
// Zastosowanie w temacie
MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
headlineLarge: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.bold,
fontSize: 28
)
)
)
)
Jeśli Flutter nie znajdzie czcionki pod wskazaną ścieżką, aplikacja użyje systemowej czcionki zastępczej bez błędów. Do debugowania włącz checkConsistency w MaterialApp — sprawdza on dostępność czcionek i wyświetla ostrzeżenia w konsoli przy brakach.
Rozmiar APK i IPA bezpośrednio zależy od liczby dołączonych czcionek. Jeden plik TTF waży 50-500 KB dla zestawu znaków łacińskich i do 2-5 MB dla czcionek z obsługą cyrylicy, chińskich znaków lub innych rozszerzonych zestawów. Dziesięć czcionek może dodać 10-20 MB do rozmiaru aplikacji, co jest krytyczne dla pobierania mobilnego.
Do optymalizacji używaj subsetting — usuwanie nieużywanych znaków z pliku czcionki. Narzędzia takie jak glyphhanger, fonttools (pyftsubset) i Google Webfont Optimizer tworzą wersję czcionki tylko z potrzebnymi glifami (łacina + cyrylica + cyfry + znaki interpunkcyjne). Zmniejsza to rozmiar o 50-80%. W iOS można używać zasobów on-demand dla czcionek, które nie są potrzebne przy pierwszym uruchomieniu.
Variable Font (czcionka zmienna) — to jeden plik zawierający wszystkie kroje: od Thin do Black i od Condensed do Expanded. Jedna czcionka zmienna może zastąpić 10-20 osobnych plików z różnymi wagami. Rozmiar takiego pliku jest w przybliżeniu równy 1-2 statycznym plikom. Obsługa: iOS 11+, Android 10+, Flutter (przez FontVariation), React Native (przez biblioteki niestandardowe).
Używaj WOFF2 do zmniejszenia rozmiaru czcionek o 30-50%. W Android z API 29 można używać WOFF2 bezpośrednio. Dla iOS wymagana jest konwersja WOFF2 do TTF przed rejestracją. Czcionki używane tylko w onboardingu lub na określonych ekranach ładuj przez zasoby on-demand (iOS) lub Dynamic Feature (Android) — nie trafią one do podstawowego APK.
Niestandardowe czcionki wpływają na wydajność na dwa sposoby: czas ładowania i pamięć. Przy pierwszym odwołaniu do czcionki system odczytuje plik z dysku, parsuje tabele czcionki i tworzy wewnętrzne struktury do renderowania. Dla czcionki o rozmiarze 1 MB zajmuje to 20-50 ms. Buforowanie po pierwszym załadowaniu eliminuje opóźnienie przy kolejnych odwołaniach.
Pamięć: każda załadowana czcionka przechowywana jest w buforze procesu. Czcionki systemowe są wstępnie załadowane, niestandardowe — ładowane przy pierwszym użyciu. W pamięci czcionka zajmuje około 2-3 razy więcej niż na dysku, ze względu na rozebrane struktury: tabele cmap, glyf, head, hmtx. Dla 5 niestandardowych czcionek średniego rozmiaru (łączny wolumen 3 MB) potrzeba około 6-9 MB RAM.
Ogranicz liczbę niestandardowych czcionek do 2-3 rodzin na aplikację. Używaj czcionek zmiennych do zastąpienia kilku krojów. Stosuj czcionki systemowe dla elementów UI (przyciski, etykiety, listy), a niestandardowe tylko do typografii akcentowej (nagłówki, banery). Buforuj Typeface w Android przez Mapę, a UIFont w iOS przez zmienne statyczne.
object FontCache {
private val cache = mutableMapOf<String, Typeface>()
fun get(context: Context, fontId: Int): Typeface {
return cache.getOrPut(fontId.toString()) {
ResourcesCompat.getFont(context, fontId)
}
}
}
// Zastosowanie w RecyclerView
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.typeface = FontCache.get(context, R.font.montserrat_regular)
}
Ładowanie Typeface w onBindViewHolder jest dopuszczalne tylko przy obecności bufora. Bez bufora każde wywołanie createFromAsset tworzy nowy obiekt i odczytuje plik z dysku. Przy scrollowaniu RecyclerView powoduje to zauważalne opóźnienia. Bufor rozwiązuje problem, ponieważ czcionka ładowana jest raz i ponownie używana.
Często zadawane pytania
OTF — optymalny wybór dla aplikacji mobilnych. Obsługuje funkcje OpenType (ligatury, alternatywne glify) przy tym samym rozmiarze co TTF. Aby zaoszczędzić miejsce, używaj czcionek zmiennych TTX lub OTF z subsetting. WOFF2 nadaje się tylko dla Android 10+ i nie jest obsługiwany w iOS w aplikacjach natywnych.
Tak. Google Fonts udostępnia czcionki na licencji OFL (Open Font License) do użytku komercyjnego. Czcionki można pobrać z fonts.google.com i dodać do projektu. Android obsługuje Downloadable Fonts przez Google Play Services, co pozwala nie dołączać plików czcionek do APK, a ładować je przy pierwszym użyciu.
Najczęstsze przyczyny: plik nie został dodany do targetu (iOS), nie wskazany w Info.plist (iOS), plik nie w res/font (Android), podano nieprawidłową nazwę PostScript, plik jest uszkodzony. Do diagnostyki w iOS wyświetl UIFont.familyNames w konsoli. W Android używaj FontLoader do debugowania ładowania czcionek z assets.
Używaj subsetting (pyftsubset) do usuwania nieużywanych znaków — zmniejsza rozmiar o 50-80%. Stosuj czcionki zmienne zamiast 10 osobnych plików. Używaj kompresji WOFF2 dla Android 10+. Skonfiguruj ProGuard do zaciemniania ścieżek do czcionek. Dla iOS używaj zasobów on-demand.
Zaleca się ograniczyć do 2-3 rodzin (wliczając różne kroje). Każda rodzina dodaje 200 KB — 3 MB do rozmiaru aplikacji. Czcionki systemowe nie obciążają aplikacji, dlatego do elementów UI używaj systemowych, a niestandardowe zostaw do nagłówków i bloków akcentowych.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również