Custom Font هو ملف خط (TTF, OTF, WOFF2) يضيفه المطور إلى تطبيق محمول لاستبدال خط النظام الافتراضي. تُستخدم الخطوط المخصصة لإنشاء نمط بصري فريد، والحفاظ على اتساق العلامة التجارية وتحسين سهولة القراءة بلغات مختلفة. وفقًا لـ Apple Fonts، فإن الخطوط المخصصة مدعومة على جميع منصات Apple، ويوفر Android آلية Font Family لتبسيط التحميل. يؤثر الدمج الصحيح للخط بشكل حاسم على الطباعة والإدراك العام للتطبيق.
النقاط الرئيسية
Custom Font (الخط المخصص) هو ملف طباعة لا يندرج ضمن مجموعة الخطوط الافتراضية لنظام التشغيل. يضيف المطورون هذه الخطوط إلى مشاريعهم للحصول على طباعة فريدة تتوافق مع العلامة التجارية. يمكن أن تكون الخطوط المخصصة إما خطوطًا تجارية مدفوعة أو خطوطًا مفتوحة المصدر مجانية، مثل Inter أو Montserrat أو إصدار مخصص من Roboto.
تشمل حالات الاستخدام النموذجية للخطوط المخصصة: شعارات وعناوين تطبيقات العلامات التجارية، تطبيقات القراءة بمتطلبات طباعية محددة، ألعاب بخطوط ذات طابع معين، وتطبيقات للغات غير اللاتينية حيث لا يدعم خط النظام الحروف الرسومية المطلوبة. وفقًا لـ Google Fonts، فإن أكثر من 60% من التطبيقات في أفضل 100 تطبيق تقوم بتخصيص خطوطها للتمييز البصري.
خطوط النظام (San Francisco على iOS، Roboto على Android) محسّنة للواجهات ولكنها غير مناسبة للطباعة الخاصة بالعلامات التجارية. الخط المخصص يعزز التعرف على العلامة التجارية، ويمكن أن يحتوي على أحرف خاصة (خطوط أيقونات) ويحسن سهولة القراءة لجماهير محددة. ومع ذلك، فإن الاستخدام المفرط للخطوط المختلفة يضعف تجربة المستخدم — يوصى باستخدام ما لا يزيد عن 2–3 عائلات لكل تطبيق.
تدعم المنصات المحمولة عدة تنسيقات للخطوط. TrueType (TTF) هو التنسيق الأكثر شيوعًا، المتوافق مع جميع إصدارات iOS وAndroid. OpenType (OTF) هو امتداد لـ TTF مع دعم الروابط والرموز البديلة وميزات OpenType. كلا التنسيقين لهما الامتدادان .ttf و.otf ويعملان بشكل متطابق على المنصات المحمولة.
WOFF2 (Web Open Font Format 2) هو تنسيق مضغوط للويب، مدعوم على Android بدءًا من الإصدار 10. يوفر WOFF2 ضغطًا بنسبة 30–50% مقارنة بـ TTF. بالنسبة لنظام iOS، WOFF2 مدعوم عبر Safari، لكن التطبيقات الأصلية تتطلب تحويلاً إلى TTF/OTF. الخطوط المتغيرة (Variable Fonts) هي تنسيق حديث يخزن جميع الأوزان في ملف واحد.
| التنسيق | iOS | Android | الضغط | الميزات |
|---|---|---|---|---|
| TTF | نعم | نعم | لا | تنسيق قياسي، توافق واسع |
| OTF | نعم | نعم | لا | ميزات OpenType، روابط، رموز بديلة |
| WOFF2 | عبر Safari | API 29+ | 30–50% | مضغوط، يوفر مساحة APK |
| Variable | iOS 11+ | API 29+ | عالٍ | ملف واحد لجميع الأوزان |
لتطوير التطبيقات المحمولة، يوصى باستخدام OTF كتنسيق رئيسي — فهو يدعم ميزات OpenType بنفس حجم TTF. لتوفير مساحة APK، استخدم الضغط عبر woff2 أو تحميل الخطوط عند الطلب.
تتكون عملية دمج خط مخصص في iOS من خطوتين: إضافة الملف إلى bundle المشروع وتسجيله في Info.plist. يتم وضع ملف الخط في دليل المشروع (عادةً Resources/Fonts) ويتم تضمينه في الهدف (target). في Info.plist، يتم إضافة مصفوفة UIAppFonts (Fonts provided by application) بأسماء ملفات الخطوط بما في ذلك الامتداد.
بعد التسجيل، يصبح الخط متاحًا عبر UIFont(name:size:) باستخدام اسم PostScript. يمكن معرفة اسم PostScript من خلال UIFont.familyNames وUIFont.fontNames(forFamilyName:). إذا كان الاسم غير صحيح، فإن UIFont(name:size:) يُرجع nil. لتصحيح الأخطاء، يوصى بعرض جميع الخطوط المسجلة على الشاشة عند التشغيل الأول.
دعنا نستعرض الدورة الكاملة لدمج خط Montserrat في مشروع iOS باستخدام Swift. يتم إضافة ملف Montserrat-Regular.ttf إلى المشروع، والإعلان عنه في Info.plist، ثم تحميله عبر UIFont وتطبيقه على UILabel. كل وزن (regular، bold، italic) يتطلب ملفًا منفصلًا وإدخالًا منفصلًا في Info.plist.
// 1. في Info.plist: UIAppFonts → "Montserrat-Regular.ttf"
// 2. تحميل الخط في الكود
guard let customFont = UIFont(name: "Montserrat-Regular", size: 16) else {
// الرجوع إلى خط النظام
label.font = UIFont.systemFont(ofSize: 16)
return
}
label.font = customFont
// 3. الاستخدام في NSAttributedString
let attributes = [NSAttributedString.Key.font: customFont]
let attributed = NSAttributedString(string: "Text", attributes: attributes)
معالجة الحالة عندما لا يتم تحميل الخط (UIFont يُرجع nil) أمر إلزامي. بدلاً من تعطل التطبيق، استخدم خط النظام كـ خط احتياطي. هذا مهم بشكل خاص للخطوط المحملة من الشبكة أو المضافة إلى وحدات اختيارية.
للخطوط التي يتم تنزيلها عند الطلب، يتم استخدام CTFontManagerRegisterGraphicsFont. تقوم هذه الطريقة بتسجيل خط من بيانات في الذاكرة (Data) وتجعله متاحًا لـ UIFont. بعد الاستخدام، يمكن إلغاء تسجيل الخط عبر CTFontManagerUnregisterGraphicsFont. التحميل الديناميكي مفيد للتطبيقات التي تحتوي على مجموعة كبيرة من الخطوط حيث لا تكون جميعها مطلوبة فورًا.
على Android، تُضاف الخطوط المخصصة عبر نظام الموارد res/font. يتم وضع ملفات TTF أو OTF في res/font/، ثم يتم إنشاء موارد XML Font Family لتجميع الأوزان. بدءًا من Android 8.0 (API 26)، هذه هي الطريقة الموصى بها الوحيدة. للإصدارات الأقدم، يتم استخدام assets وTypeface.createFromAsset.
تصف XML Font Family التطابق بين الوزن (regular، bold، italic) وملف الخط. في تخطيط XML، السمة android:fontFamily="@font/my_font" تحدد تلقائيًا الملف الصحيح عند استدعاء setTypeface بالنمط المقابل. هذا يبسط الطباعة — يكفي تحديد العائلة في السمة (theme) وسيختار Android الإصدار bold عند استدعاء setTypeface(textView, Typeface.BOLD).
قم بإنشاء ملف XML في res/font/my_font.xml يسرد جميع ملفات الخطوط للأوزان المختلفة. كل عنصر له سمات fontStyle (normal/italic) وfontWeight (100–900). ثم في التخطيط، حدد android:fontFamily="@font/my_font". عند استخدام textStyle="bold"، يختار Android تلقائيًا الملف الذي يحتوي على 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>
عند استخدام Font Family، فإن نهج الموارد يخزن الخطوط مؤقتًا تلقائيًا ويختار الوزن الصحيح. إذا كان الوزن المطلوب غير متوفر في العائلة، يقوم Android بتركيبه من أقرب وزن متاح. للتحكم الدقيق في الخط، استخدم Typeface.create() مع مرجع ملف صريح.
في الأطر متعددة المنصات، يكون دمج الخطوط موحدًا. Flutter يستخدم pubspec.yaml للإعلان عن الخطوط: توضع الملفات في مجلد fonts/ للمشروع ثم تُعلن في قسم fonts مع العائلة والأوزان. ثم يتم تطبيق الخط عبر TextStyle(fontFamily: 'Montserrat') أو في سمة التطبيق. Flutter يدعم TTF وOTF.
React Native يستخدم نهجين: الأصلي (عبر Info.plist وres/font) أو عبر مكتبات مثل react-native-vector-icons و@expo-google-fonts. Expo يبسط العملية — يتم تحميل الخطوط عبر expo-font: Font.loadAsync({ 'Montserrat': require('./assets/fonts/Montserrat.ttf') }). React Navigation والسمات تسمح بتعيين خط بشكل عام.
الإعلان عن الخطوط في pubspec.yaml وتطبيقها في سمة MaterialApp. توضع ملفات Montserrat-Regular.ttf وMontserrat-Bold.ttf في fonts/. بعد الإعلان، يصبح الخط متاحًا عبر TextStyle في جميع أنحاء التطبيق. Flutter يستخدم تلقائيًا ملف bold عند تحديد fontWeight: FontWeight.bold.
// pubspec.yaml
flutter:
fonts:
- family: Montserrat
fonts:
- asset: fonts/Montserrat-Regular.ttf
- asset: fonts/Montserrat-Bold.ttf
weight: 700
// الاستخدام في السمة
MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
headlineLarge: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.bold,
fontSize: 28
)
)
)
)
إذا لم يعثر Flutter على خط في المسار المحدد، يستخدم التطبيق خط النظام الاحتياطي دون أخطاء. لتصحيح الأخطاء، قم بتفعيل checkConsistency في MaterialApp — فهو يتحقق من توفر الخطوط ويطبع تحذيرات في وحدة التحكم عند فقدان الخطوط.
حجم APK وIPA يعتمد بشكل مباشر على عدد الخطوط المضمنة. ملف TTF واحد يزن 50–500 كيلوبايت لمجموعة الأحرف اللاتينية وما يصل إلى 2–5 ميجابايت للخطوط التي تدعم السيريلية أو أحرف CJK أو مجموعات ممتدة أخرى. عشرة خطوط يمكن أن تضيف 10–20 ميجابايت إلى حجم التطبيق، وهو أمر بالغ الأهمية للتنزيلات المحمولة.
للتحسين، استخدم subsetting — إزالة الأحرف غير المستخدمة من ملف الخط. أدوات مثل glyphhanger وfonttools (pyftsubset) وGoogle Webfont Optimizer تنشئ إصدارًا من الخط يحتوي فقط على الحروف الرسومية المطلوبة (لاتينية + سيريلية + أرقام + علامات ترقيم). هذا يقلل الحجم بنسبة 50–80%. على iOS، يمكن استخدام الموارد عند الطلب للخطوط غير المطلوبة عند التشغيل الأول.
الخط المتغير (Variable Font) هو ملف واحد يحتوي على جميع الأوزان من Thin إلى Black ومن Condensed إلى Expanded. يمكن لخط متغير واحد أن يحل محل 10–20 ملفًا منفصلاً بأوزان مختلفة. حجم هذا الملف يساوي تقريبًا 1–2 ملفًا ثابتًا. الدعم: iOS 11+، Android 10+، Flutter (عبر FontVariation)، React Native (عبر مكتبات مخصصة).
استخدم WOFF2 لتقليل حجم الخطوط بنسبة 30–50%. على Android مع API 29، يمكن استخدام WOFF2 مباشرة. بالنسبة لنظام iOS، يجب تحويل WOFF2 إلى TTF قبل التسجيل. الخطوط المستخدمة فقط أثناء الإعداد أو على شاشات معينة يجب تحميلها عبر الموارد عند الطلب (iOS) أو Dynamic Feature (Android) — لن يتم تضمينها في APK الأساسي.
تؤثر الخطوط المخصصة على الأداء بطريقتين: وقت التحميل والذاكرة. عند الوصول الأول، يقرأ النظام الملف من القرص، ويحلل جداول الخط وينشئ هياكل داخلية للعرض. بالنسبة لخط بحجم 1 ميجابايت، يستغرق ذلك 20–50 مللي ثانية. التخزين المؤقت بعد التحميل الأول يزيل التأخير في الوصولات اللاحقة.
الذاكرة: يتم تخزين كل خط محمّل في ذاكرة التخزين المؤقت للعملية. الخطوط النظامية محملة مسبقًا؛ الخطوط المخصصة تُحمّل عند الاستخدام الأول. في الذاكرة، يشغل الخط مساحة أكبر بمقدار 2–3 مرات تقريبًا مما هو عليه على القرص بسبب الهياكل التي تم تحليلها: جداول cmap وglyf وhead وhmtx. بالنسبة لـ 5 خطوط مخصصة متوسطة الحجم (إجمالي 3 ميجابايت)، يلزم حوالي 6–9 ميجابايت من RAM.
حدد الخطوط المخصصة إلى 2–3 عائلات لكل تطبيق. استخدم الخطوط المتغيرة لاستبدال عدة أوزان. طبق خطوط النظام لعناصر الواجهة (أزرار، تسميات، قوائم) والخطوط المخصصة فقط للطباعة البارزة (عناوين، لافتات). قم بتخزين Typeface مؤقتًا على Android عبر Map وUIFont على iOS عبر متغيرات ثابتة.
object FontCache {
private val cache = mutableMapOf<String, Typeface>()
fun get(context: Context, fontId: Int): Typeface {
return cache.getOrPut(fontId.toString()) {
ResourcesCompat.getFont(context, fontId)
}
}
}
// الاستخدام في RecyclerView
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.typeface = FontCache.get(context, R.font.montserrat_regular)
}
تحميل Typeface في onBindViewHolder مقبول فقط مع وجود cache. بدون cache، كل استدعاء createFromAsset ينشئ كائنًا جديدًا ويقرأ الملف من القرص. أثناء التمرير في RecyclerView، يسبب هذا تأخيرات ملحوظة. يحل cache المشكلة لأن الخط يُحمّل مرة واحدة ويُعاد استخدامه.
الأسئلة الشائعة
OTF هو الخيار الأمثل للتطبيقات المحمولة. يدعم ميزات OpenType (الروابط، الرموز البديلة) بنفس حجم TTF. لتوفير المساحة، استخدم الخطوط المتغيرة (TTX أو OTF) مع subsetting. WOFF2 مناسب فقط لـ Android 10+ وغير مدعوم على iOS في التطبيقات الأصلية.
نعم. توفر Google Fonts خطوطًا بموجب ترخيص OFL (Open Font License) للاستخدام التجاري. يمكن تنزيل الخطوط من fonts.google.com وإضافتها إلى المشروع. يدعم Android الخطوط القابلة للتنزيل عبر Google Play Services، مما يسمح بعدم تضمين ملفات الخطوط في APK وتحميلها عند الاستخدام الأول.
الأسباب الأكثر شيوعًا: الملف غير مضاف إلى الهدف (target) في iOS، غير مُعلن في Info.plist (iOS)، الملف ليس في res/font (Android)، اسم PostScript غير صحيح، أو الملف تالف. للتشخيص على iOS، اطبع UIFont.familyNames في وحدة التحكم. على Android، استخدم FontLoader لتصحيح أخطاء تحميل الخطوط من assets.
استخدم subsetting (pyftsubset) لإزالة الأحرف غير المستخدمة — يقلل الحجم بنسبة 50–80%. استخدم الخطوط المتغيرة بدلاً من 10 ملفات منفصلة. استخدم ضغط WOFF2 لـ Android 10+. قم بتكوين ProGuard لإخفاء مسارات الخطوط. لنظام iOS، استخدم الموارد عند الطلب.
يوصى بالاقتصار على 2–3 عائلات (بما في ذلك الأوزان المختلفة). كل عائلة تضيف 200 كيلوبايت إلى 3 ميجابايت إلى حجم التطبيق. خطوط النظام لا تثقل التطبيق، لذا استخدم خطوط النظام لعناصر الواجهة واحتفظ بالخطوط المخصصة للعناوين والكتل البارزة.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا