ParagraphStyle — المفاهيم الأساسية، الخصائص والاستخدام

المؤلف: IT Sectr نُشر: 2026-07-25 وقت القراءة: 10 دق

ParagraphStyle هو مجموعة من معلمات التنسيق التي تحدد المظهر المرئي لفقرة نصية. في تطوير تطبيقات الجوال، يتضمن ParagraphStyle المحاذاة (left, center, right, justified)، والتباعد بين الأسطر، والمسافة البادئة للسطر الأول، والمسافات بين الفقرات، واتجاه النص. وفقًا لإرشادات التصميم المادي للطباعة (2025)، يضمن الاستخدام المتسق لـ ParagraphStyle في جميع شاشات التطبيق إيقاعًا طباعيًا موحدًا ويقلل الضوضاء البصرية عند التبديل بين الأقسام.

النقاط الرئيسية

  • ParagraphStyle — كائن إعدادات تنسيق الفقرة: المحاذاة، المسافات البادئة، التباعد.
  • المنصات — iOS: NSMutableParagraphStyle، Android: ParagraphStyle (Compose) / Layout (View)، Flutter: TextStyle.
  • المحاذاة — left للنص الرئيسي، center للعناوين، justified لتنسيق الكتاب.
  • المسافات البادئة — firstLineHeadIndent (مسافة السطر الأول)، paragraphSpacing (التباعد بين الفقرات).
  • التدويل — يراعي ParagraphStyle اتجاه النص (LTR/RTL) للغات المختلفة.

ما هو ParagraphStyle في الطباعة

ParagraphStyle (نمط الفقرة) هو مجموعة من قواعد التنسيق القابلة للتطبيق على فقرة واحدة أو أكثر من النص. على عكس أنماط الأحرف (bold, italic, font-size) التي تعمل على مستوى الأحرف الفردية، يدير ParagraphStyle الخصائص الكلية: وضع النص داخل الحاوية، والمسافات بين الأسطر والفقرات، واتجاه الكتابة، وقواعد الوصل.

تاريخيًا، جاء مفهوم ParagraphStyle من أنظمة النشر المكتبي (QuarkXPress, PageMaker, InDesign)، حيث كان لكل فقرة مجموعتها الخاصة من المعلمات. في تطوير تطبيقات الجوال، تحول هذا النهج إلى واجهات برمجية: NSMutableParagraphStyle على iOS، وفئات ParagraphStyle و LineBreakStyle في Android، ومعلمات مثل textAlign و textHeightBehavior في Flutter. جميعها تخدم غرضًا واحدًا — إدارة تنسيق الفقرة من خلال الكود، دون الارتباط بمحرك نصي محدد.

وفقًا لـ Bringhurst — The Elements of Typographic Style (2024)، يعد التكوين الصحيح لـ ParagraphStyle أساس الثقافة الطباعية للواجهة. إذا كان نوع الخط وحجمه يحددان «صوت» النص، فإن نمط الفقرة يحدد «تنفسه»: الإيقاع، التوقفات، والتركيز. في واجهات الجوال حيث المساحة محدودة، يسمح ParagraphStyle الصحيح بتضمين المزيد من المعلومات دون فقدان readability.

الخصائص الرئيسية لـ ParagraphStyle

ParagraphStyle يتضمن عدة مجموعات من الخصائص، كل منها مسؤولة عن جانب معين من التنسيق. دعنا نلقي نظرة على المعلمات الرئيسية المتاحة في واجهات برمجة التطبيقات للجوال. المحاذاة هي المعلمة الرئيسية التي تحدد الموضع الأفقي للنص. على iOS، يتوفر NSTextAlignment، على Android — Layout.Alignment، في Flutter — TextAlign.

الخاصيةiOS (NSMutableParagraphStyle)Android (Compose)Flutter (TextStyle)
المحاذاةalignmenttextAligntextAlign
المسافة البادئة اليسرىheadIndenttextIndent (في Compose)
المسافة البادئة اليمنىtailIndent
مسافة السطر الأولfirstLineHeadIndenttextIndent (firstLine)
التباعد بين الفقراتparagraphSpacingparagraphStyle (lineHeight)
التباعد قبل الفقرةparagraphSpacingBefore
اتجاه النصbaseWritingDirectionTextDirection (Compose)textDirection
وضع فاصل السطرlineBreakModeoverflow (TextStyle)overflow (TextStyle)

من المهم أن نفهم أن ليست كل الخصائص متاحة على جميع المنصات بنفس القدر. على سبيل المثال، مسافة السطر الأول (firstLineHeadIndent) مدعومة بالكامل فقط على iOS عبر NSMutableParagraphStyle. في Android، يتم استخدام SpannableString مع LeadingMarginSpan لهذا الغرض، وفي Flutter — RichText مع TextSpan مخصص و padding. في التطوير عبر المنصات (Flutter, React Native)، يجب محاكاة بعض خصائص ParagraphStyle من خلال الحاويات أو Spacer.

ParagraphStyle في iOS: NSMutableParagraphStyle

في iOS، الأداة الرئيسية للعمل مع نمط الفقرة هي فئة NSMutableParagraphStyle — فئة فرعية من NSParagraphStyle. توفر مجموعة كاملة من الخصائص لإدارة تنسيق الفقرة ويتم تطبيقها عبر NSAttributedString. يتم استخدام NSMutableParagraphStyle ليس فقط في UILabel و UITextView، ولكن أيضًا في Core Text عبر CTParagraphStyle.

swift
// iOS: تكوين ParagraphStyle كامل
let paragraphStyle = NSMutableParagraphStyle()

// المحاذاة
paragraphStyle.alignment = .justified     // مضبوط

// المسافة البادئة
paragraphStyle.headIndent = 16          // مسافة بادئة يسرى
paragraphStyle.tailIndent = -16         // مسافة بادئة يمنى (سلبية)
paragraphStyle.firstLineHeadIndent = 32 // مسافة السطر الأول

// التباعد بين الفقرات
paragraphStyle.paragraphSpacing = 12    // بعد الفقرة
paragraphStyle.paragraphSpacingBefore = 8 // قبل الفقرة

// ارتفاع السطر
paragraphStyle.minimumLineHeight = 24
paragraphStyle.maximumLineHeight = 24

let attributes: [NSAttributedString.Key: Any] = [
    .font: UIFont.systemFont(ofSize: 16),
    .paragraphStyle: paragraphStyle
]

let label = UILabel()
label.attributedText = NSAttributedString(
    string: "This paragraph has a custom ParagraphStyle.\n\nSecond paragraph with 12 pt spacing.",
    attributes: attributes
)

في SwiftUI، لا يوجد مقابل مباشر لـ NSMutableParagraphStyle — بدلاً من ذلك، يتم استخدام مجموعة من المعدلات: .multilineTextAlignment() للمحاذاة، .lineSpacing() للتباعد بين الأسطر. لمسافة السطر الأول والتباعد بين الفقرات في SwiftUI، يجب استخدام Text + padding أو AttributedString (متاح منذ iOS 15+)، الذي يدعم NSParagraphStyle من خلال السمات.

swift
// SwiftUI: AttributedString مع ParagraphStyle (iOS 15+)
var attributed: AttributedString = """
This is the first paragraph with indent.

This is the second paragraph with 12 pt spacing.
"""

let ps = NSMutableParagraphStyle()
ps.paragraphSpacing = 12
ps.firstLineHeadIndent = 20

let container = AttributeContainer()
    .paragraphStyle(ps)

attributed.mergeAttributes(container)

Text(attributed)
    .font(.body)

وفقًا لـ Apple — Text Programming Guide (2025)، يدعم NSMutableParagraphStyle ما يصل إلى 14 معلمة تنسيق مختلفة. من المهم تذكر أن tailIndent يتم تعيينه كقيمة سالبة بالنسبة لعرض الحاوية، بينما headIndent موجب. هذا السلوك غير الواضح غالبًا ما يسبب أخطاء لمطوري iOS المبتدئين.

ParagraphStyle في Android: نظام View و Compose

في Android، يختلف نهج ParagraphStyle بين نظام View و Jetpack Compose. في نظام View الكلاسيكي، يتم تعيين معلمات الفقرة من خلال سمات XML لـ TextView (android:textAlignment, android:lineSpacingExtra) أو من خلال SpannableString مع تطبيقات واجهة ParagraphStyle من حزمة android.text.style.

kotlin
// نظام Android View: ParagraphStyle عبر SpannableString
val text = "First paragraph with settings.\n\nSecond paragraph."
val spannable = SpannableString(text)

// محاذاة مضبوطة
spannable.setSpan(
    AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
    0, text.length,
    Spannable.SPAN_PARAGRAPH
)

// مسافة السطر الأول عبر LeadingMarginSpan
spannable.setSpan(
    LeadingMarginSpan.Standard(0, 20),
    0, text.indexOf('\n'),
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)

textView.text = spannable

في Jetpack Compose، يتم إدارة ParagraphStyle من خلال معلمات TextStyle. يتم استخدام textAlign للمحاذاة، lineHeight (بـ sp أو em) للتباعد بين الأسطر، و textIndent لمسافة السطر الأول. يدعم Compose أيضًا LineBreak للتحكم الدقيق في فواصل الأسطر (Simple, Heading, Paragraph)، وهو مفيد بشكل خاص للنص متعدد اللغات.

kotlin
// Jetpack Compose: ParagraphStyle عبر TextStyle
Text(
    text = "الفقرة الأولى بنمط مخصص.

الفقرة الثانية بمسافة بادئة.",
    style = TextStyle(
        textAlign = TextAlign.Justify,
        lineHeight = 24.sp,
        textIndent = TextIndent(firstLine = 16.sp),
        lineBreak = LineBreak.Paragraph
    )
)

وفقًا لـ Android Developers — Compose ParagraphStyle (2025)، عند استخدام TextIndent في Compose، من المهم تعيين firstLine بـ sp (وليس dp)، حيث يجب أن تتدرج مسافة السطر الأول مع النص. إذا تم تعيين المسافة البادئة بـ dp، فعند زيادة حجم الخط، ستصبح مسافة السطر الأول أصغر بصريًا، مما يخل بالنسب الطباعية.

ParagraphStyle في Flutter: TextHeightBehavior و StrutStyle

في Flutter، لم يتم فصل نموذج ParagraphStyle في فئة مخصصة — بدلاً من ذلك، يتم استخدام مجموعة من معلمات TextStyle وفئات إضافية. يتم تعيين المحاذاة عبر TextAlign، والتباعد بين الأسطر عبر height (بدون وحدة)، ويتحكم TextHeightBehavior في المسافات البادئة المرئية للسطر الأول والأخير.

dart
// Flutter: ParagraphStyle عبر معلمات Text
Text(
    'فقرة منسقة في Flutter.

الفقرة الثانية بمسافات بادئة.',
    style: TextStyle(
        fontSize: 16,
        height: 1.5,
        leadingDistribution: TextLeadingDistribution.proportional,
    ),
    textAlign: TextAlign.justify,
    textHeightBehavior: TextHeightBehavior(
        applyHeightToFirstAscent: true,
        applyHeightToLastDescent: true,
    ),
)

للسيناريوهات الأكثر تعقيدًا، يقدم Flutter استخدام RichText مع TextSpan و WidgetSpan مخصصين. يتيح ذلك دمج النص مع عناصر واجهة مستخدم مضمنة (أيقونات، صور) في فقرة واحدة، مع الحفاظ على قواعد تنسيق موحدة. ومع ذلك، يتطلب هذا النهج تحكمًا يدويًا في فواصل الأسطر ولا يدعم الوصل التلقائي.

dart
// Flutter: RichText مع تنسيق فقرة مخصص
RichText(
    text: TextSpan(
        style: TextStyle(fontSize: 16, height: 1.5),
        children: [
            TextSpan(text: 'First paragraph with icon '),
            WidgetSpan(
                child: Icon(Icons.star, size: 16),
                alignment: PlaceholderAlignment.middle,
            ),
            TextSpan(text: '\n\nSecond paragraph after spacing.'),
        ],
    ),
)

وفقًا لـ Flutter Documentation — Text Widget (2025)، TextHeightBehavior هو معلمة رئيسية لعرض ParagraphStyle بشكل صحيح في Flutter. إذا تم تعيين applyHeightToFirstAscent على false، يتم تجاهل المسافة البادئة العلوية للسطر الأول، و «يلتصق» النص بالحد العلوي للحاوية. هذا مفيد للعناوين، لكنه غير مرغوب فيه للنص الرئيسي.

ParagraphStyle والتدويل: LTR/RTL والخصائص الإقليمية

ParagraphStyle يجب أن يراعي اتجاه الكتابة لكل لغة. في تطبيقات الجوال التي تدعم العربية أو العبرية أو الأردية (الكتابة من اليمين إلى اليسار، RTL)، يجب أن تنعكس المسافات البادئة والمحاذاة بشكل معكوس. يدعم iOS التبديل التلقائي عبر baseWritingDirection = .natural، و Android عبر TextDirection، و Flutter عبر textDirection.

يتطلب تكوين المحاذاة للغات RTL اهتمامًا خاصًا. إذا كان headIndent في وضع LTR يحدد المسافة البادئة اليسرى، فإنه في RTL يصبح تلقائيًا المسافة البادئة اليمنى. ومع ذلك، لا تعالج جميع التطبيقات هذا التحول بشكل صحيح عند تحديد المحاذاة صراحة (NSTextAlignment.left بدلاً من NSTextAlignment.natural). وفقًا لـ Google Internationalization Guide (2025)، للتطبيقات متعددة اللغات، استخدم دائمًا المحاذاة الطبيعية وتحقق من المسافات البادئة في كلا التخطيطين.

kotlin
// Android: دعم RTL في ParagraphStyle
val textView = TextView(context)

// تمكين دعم RTL التلقائي
textView.textDirection = View.TEXT_DIRECTION_LOCALE
textView.textAlignment = View.TEXT_ALIGNMENT_TEXT_START

// Spannable مع دعم RTL
val spannable = SpannableString(arabicText)
spannable.setSpan(
    AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
    0, arabicText.length,
    Spannable.SPAN_PARAGRAPH
)

تتضمن الخصائص الإقليمية أيضًا قواعد مختلفة لمسافة السطر الأول. في الطباعة الأوروبية، مسافة السطر الأول (firstLineHeadIndent) هي معيار لفصل الفقرات. في الطباعة اليابانية والصينية، تُستخدم مسافة السطر الأول أيضًا، لكن حجمها قد يختلف (عادة 1 em مقابل 1.5 em في التقاليد الأوروبية). للطباعة العربية، مسافة السطر الأول نادرة — عادة ما تُفصل الفقرات بمسافة رأسية.

الأخطاء الشائعة عند العمل مع ParagraphStyle

الخطأ الأكثر شيوعًا هو استخدام ParagraphStyle فقط من خلال السمات العامة لحقل النص دون مراعاة التنسيق المختلف للفقرات الفردية. في UILabel أو TextView مع عدة فقرات مفصولة بـ ، يتم تطبيق ParagraphStyle على النص بأكمله بشكل موحد ما لم يتم استخدام نص منسق بأنماط مختلفة لكل فقرة.

  • تجاهل paragraphSpacingBefore — العديد من المطورين يحددون paragraphSpacing لكنهم ينسون paragraphSpacingBefore. للفقرة الأولى في كتلة، يسمح paragraphSpacingBefore بتعيين هامش علوي دون إنشاء حاوية إضافية. هذا مفيد بشكل خاص للقوائم ذات العناوين.
  • المحاذاة الثابتة (left/right) بدلاً من الطبيعية — إذا كان التطبيق يدعم لغات RTL، فإن استخدام NSTextAlignment.left أو TextAlign.Start بدلاً من natural يؤدي إلى عرض غير صحيح في اللغات العربية والعبرية.
  • عدم التحقق من وضع فاصل السطر — في الحاويات الضيقة (القوائم، الأشرطة الجانبية)، يؤدي lineBreakMode غير الصحيح إلى اقتطاع الكلمات. استخدم byWordWrapping وتحقق من الكلمات الطويلة في سياق اللغة المحددة.
  • خلط Spannable مع ParagraphStyle مختلف — عند تعيين Spannable مع SPAN_PARAGRAPH، من المهم حساب حدود الفقرات بشكل صحيح. عدم تطابق مواضع البداية/النهاية مع الحدود الفعلية للفقرات يؤدي إلى تجاهل النمط.
  • نسيان محاذاة خط الأساس — عند استخدام ParagraphStyle مع lineHeight ثابت (minimumLineHeight)، تأكد من تمكين محاذاة النص مع خط الأساس. في iOS، يتم استخدام label.baselineAdjustment = .alignBaselines.

وفقًا لـ UX Collective — Common Typography Bugs in Mobile Apps (2025)، تشكل مشكلات ParagraphStyle 18% من جميع الأخطاء الطباعية في تطبيقات الجوال. الحالات الأكثر خطورة هي عندما يؤدي paragraphSpacing غير الصحيح إلى تداخل النص على أسطر مختلفة أو عندما يتم عرض لغات RTL بمسافات بادئة LTR. يُوصى بإنشاء اختبارات وحدة لكل معلمة من معلمات ParagraphStyle مع سلاسل اختبار بلغات مختلفة.

الأسئلة الشائعة

ما الفرق بين ParagraphStyle و TextStyle في iOS؟

NSMutableParagraphStyle يدير تنسيق الفقرة (المحاذاة، المسافات البادئة، التباعد بين الأسطر)، بينما TextStyle يدير سمات الأحرف (الخط، اللون، الحجم). يتم تطبيق كلاهما عبر NSAttributedString. يعمل ParagraphStyle على مستوى الفقرة، و TextStyle على مستوى الأحرف. يمكن أن توجد عدة مثيلات لـ TextStyle في فقرة واحدة، لكن ParagraphStyle واحد فقط لكل فقرة.

كيفية تعيين مسافة السطر الأول في Jetpack Compose؟

في Jetpack Compose، يتم تعيين مسافة السطر الأول من خلال TextIndent داخل TextStyle: TextStyle(textIndent = TextIndent(firstLine = 16.sp)). يجب أن تكون القيمة بـ sp لتتدرج مع النص. للنص متعدد الأسطر، تُطبق المسافة البادئة على كل سطر يبدأ فقرة جديدة (حرف ). للفقرة الأولى، تُطبق المسافة البادئة دائمًا.

كيفية تكوين التباعد بين الفقرات في SwiftUI؟

في SwiftUI، لا يوجد مقابل مباشر لـ paragraphSpacing. للتباعد بين الفقرات، استخدم AttributedString (iOS 15+) مع NSMutableParagraphStyle وخاصية paragraphSpacing. أو افصل الفقرات عبر Text("...\n\n...") وأضف .padding() بين كتل النص. للسيناريوهات الطباعية المعقدة، استخدم NSAttributedString عبر UILabelRepresentable.

لماذا لا يعمل paragraphSpacing في Android TextView؟

TextView في Android لا يحتوي على خاصية paragraphSpacing — يتم تعيينها من خلال SpannableString مع LeadingMarginSpan أو من خلال LineBackgroundSpan مخصص. للتباعد بين الأسطر استخدم setLineSpacing، للتباعد بين الفقرات — افصل الفقرات بـ إضافي أو استخدم حاوية مركبة من عدة مثيلات TextView.

كيف يؤثر ParagraphStyle على إمكانية الوصول لتطبيق الجوال؟

يعمل ParagraphStyle الصحيح على تحسين إمكانية الوصول: يساعد paragraphSpacing الكافي المستخدمين ذوي الإعاقات المعرفية على تمييز حدود الفقرات بشكل أفضل. يوصي WCAG 2.2 بالفصل البصري للفقرات (مسافة بادئة أو تباعد). يجب ألا يقل paragraphSpacing عن 1.5 × line-height. للغات RTL، تحقق من أن المسافات البادئة تنعكس بشكل صحيح.

الملخص

  • ParagraphStyle — مجموعة قواعد تنسيق الفقرة: المحاذاة، المسافات البادئة، التباعد بين الأسطر والفقرات.
  • iOS — NSMutableParagraphStyle مع 14+ معلمة، يُطبق عبر NSAttributedString؛ في SwiftUI — AttributedString (iOS 15+).
  • Android View — SpannableString مع تطبيقات ParagraphStyle (AlignmentSpan, LeadingMarginSpan).
  • Android Compose — TextStyle.textAlign، lineHeight، textIndent و LineBreak للتحكم في فواصل الأسطر.
  • Flutter — مزيج من TextStyle.height، TextAlign، TextHeightBehavior و StrutStyle لتثبيت ارتفاع السطر.
  • التدويل — استخدم المحاذاة الطبيعية لدعم لغات RTL؛ تحقق من المسافات البادئة في كلا التخطيطين.
  • الأخطاء الشائعة — المحاذاة الثابتة بدلاً من الطبيعية، إهمال paragraphSpacingBefore، lineBreakMode غير صحيح للحاويات الضيقة.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا