ParagraphStyle — مجموعهای از پارامترهای قالببندی است که ظاهر بصری پاراگراف متن را تعیین میکند. در توسعه موبایل، ParagraphStyle شامل تراز (left, center, right, justified)، فاصله بین خطوط، تورفتگی خط اول، فاصله بین پاراگرافها و جهت متن است. طبق Material Design Typography Guidelines (2025)، استفاده منسجم از ParagraphStyle در تمام صفحههای برنامه، ریتم تایپوگرافیک یکنواختی را تضمین کرده و نویز بصری هنگام جابجایی بین بخشها را کاهش میدهد.
نکات اصلی
ParagraphStyle (سبک پاراگراف) — مجموعهای از قوانین قالببندی است که به یک یا چند پاراگراف متن اعمال میشود. برخلاف سبکهای کاراکتری (bold, italic, font-size) که در سطح کاراکترهای منفرد عمل میکنند، ParagraphStyle ویژگیهای کلان را مدیریت میکند: قرارگیری متن در ظرف، فاصلههای بین خطوط و پاراگرافها، جهت نوشتار و قوانین شکستن کلمات.
از نظر تاریخی، مفهوم ParagraphStyle از سیستمهای نشر رومیزی (QuarkXPress, PageMaker, InDesign) آمده است، جایی که هر پاراگراف مجموعه پارامترهای خاص خود را داشت. در توسعه موبایل، این رویکرد به APIهای برنامهنویسی تبدیل شد: NSMutableParagraphStyle در iOS، کلاسهای ParagraphStyle و LineBreakStyle در Android، و همچنین پارامترهایی مانند textAlign و textHeightBehavior در Flutter. همه آنها یک وظیفه را حل میکنند — مدیریت قالببندی پاراگراف از طریق کد، بدون وابستگی به موتور متنی خاص.
طبق Bringhurst — The Elements of Typographic Style (2024)، پیکربندی صحیح ParagraphStyle اساس فرهنگ تایپوگرافیک رابط کاربری است. اگر فونت و اندازه مسئول «صدای» متن هستند، سبک پاراگراف مسئول «نفس» آن است: ریتم، مکثها و تأکیدها. در رابطهای موبایل که فضا محدود است، ParagraphStyle مناسب امکان قرار دادن اطلاعات بیشتر را بدون کاهش خوانایی فراهم میکند.
ParagraphStyle شامل چند گروه از ویژگیها است که هر کدام مسئول جنبه خاصی از قالببندی هستند. بیایید پارامترهای کلیدی موجود در APIهای موبایل را بررسی کنیم. تراز (alignment) — پارامتر اصلی که موقعیت افقی متن را تعیین میکند. در iOS، NSTextAlignment، در Android، Layout.Alignment، در Flutter، TextAlign در دسترس است.
| ویژگی | iOS (NSMutableParagraphStyle) | Android (Compose) | Flutter (TextStyle) |
|---|---|---|---|
| تراز | alignment | textAlign | textAlign |
| تورفتگی چپ | headIndent | textIndent (در Compose) | — |
| تورفتگی راست | tailIndent | — | — |
| تورفتگی خط اول | firstLineHeadIndent | textIndent (firstLine) | — |
| فاصله بین پاراگرافها | paragraphSpacing | paragraphStyle (lineHeight) | — |
| فاصله قبل از پاراگراف | paragraphSpacingBefore | — | — |
| جهت متن | baseWritingDirection | TextDirection (Compose) | textDirection |
| حالت شکستن خط | lineBreakMode | overflow (TextStyle) | overflow (TextStyle) |
درک این نکته مهم است که همه ویژگیها در همه پلتفرمها به یک اندازه در دسترس نیستند. به عنوان مثال، تورفتگی خط اول (firstLineHeadIndent) به طور کامل فقط در iOS از طریق NSMutableParagraphStyle پشتیبانی میشود. در Android برای این منظور از SpannableString با LeadingMarginSpan و در Flutter از RichText با TextSpan سفارشی و padding استفاده میشود. در توسعه بینپلتفرمی (Flutter, React Native) برخی از ویژگیهای ParagraphStyle باید از طریق کانتینرها یا Spacer شبیهسازی شوند.
در iOS ابزار اصلی کار با سبک پاراگراف کلاس NSMutableParagraphStyle — زیرکلاس NSParagraphStyle است. این کلاس مجموعه کاملی از ویژگیها را برای مدیریت قالببندی پاراگراف ارائه میدهد و از طریق NSAttributedString اعمال میشود. NSMutableParagraphStyle نه تنها در UILabel و UITextView، بلکه در Core Text از طریق CTParagraphStyle استفاده میشود.
// iOS: پیکربندی کامل ParagraphStyle
let paragraphStyle = NSMutableParagraphStyle()
// تراز
paragraphStyle.alignment = .justified // 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 وجود ندارد — در عوض از ترکیبی از modifierها استفاده میشود: .multilineTextAlignment() برای تراز، .lineSpacing() برای فاصله بین خطوط. برای تورفتگی خط اول و فاصله بین پاراگرافها در SwiftUI باید از Text + padding یا AttributedString (موجود از iOS 15+) استفاده کرد.
// 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 میشود.
در Android رویکرد به ParagraphStyle بین View-system و Jetpack Compose متفاوت است. در View-system کلاسیک، پارامترهای پاراگراف از طریق ویژگیهای XML TextView (android:textAlignment, android:lineSpacingExtra) یا از طریق SpannableString با پیادهسازیهای رابط ParagraphStyle از بسته android.text.style تنظیم میشوند.
// Android View system: ParagraphStyle از طریق SpannableString
val text = "First paragraph with settings.\n\nSecond paragraph."
val spannable = SpannableString(text)
// تراز justified
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) پشتیبانی میکند که برای متن چندزبانه مفید است.
// Jetpack Compose: ParagraphStyle از طریق TextStyle
Text(
text = "پاراگراف اول با سبک سفارشی.\n\nپاراگراف دوم با تورفتگی.",
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 تنظیم شود، با افزایش اندازه فونت، تورفتگی خط اول از نظر بصری کوچکتر میشود که نسبتهای تایپوگرافیک را نقض میکند.
در Flutter پارادایم ParagraphStyle در یک کلاس جداگانه تفکیک نشده است — در عوض از ترکیب پارامترهای TextStyle و کلاسهای اضافی استفاده میشود. تراز از طریق TextAlign، فاصله بین خطوط از طریق height (بدون واحد)، و TextHeightBehavior تورفتگیهای قابل مشاهده خط اول و آخر را مدیریت میکند.
// Flutter: ParagraphStyle از طریق پارامترهای Text
Text(
'پاراگراف styled در Flutter.\n\nپاراگراف دوم با تورفتگی.',
style: TextStyle(
fontSize: 16,
height: 1.5,
leadingDistribution: TextLeadingDistribution.proportional,
),
textAlign: TextAlign.justify,
textHeightBehavior: TextHeightBehavior(
applyHeightToFirstAscent: true,
applyHeightToLastDescent: true,
),
)
برای سناریوهای پیچیدهتر، Flutter استفاده از RichText با TextSpan و WidgetSpan سفارشی را پیشنهاد میکند. این امکان ترکیب متن با ویجتهای درونخطی (آیکونها، تصاویر) در یک پاراگراف را با حفظ قوانین یکنواخت قالببندی فراهم میکند. با این حال، این رویکرد نیاز به مدیریت دستی شکستن کلمات دارد و از تنظیم خودکار شکستن کلمات پشتیبانی نمیکند.
// 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 باید جهت نوشتار را برای هر بومیسازی در نظر بگیرد. در برنامههای موبایل با پشتیبانی از عربی، عبری یا اردو (نوشتار راست به چپ، RTL)، تورفتگیها و تراز باید به صورت آینهای تغییر کنند. iOS از switching خودکار از طریق baseWritingDirection = .natural، Android از طریق TextDirection و Flutter از طریق textDirection پشتیبانی میکند.
تنظیم تراز برای بومیسازیهای RTL نیاز به توجه ویژه دارد. اگر در حالت LTR، headIndent تورفتگی چپ را تنظیم میکند، در RTL به طور خودکار به راست تبدیل میشود. با این حال، همه پیادهسازیها این انتقال را با تنظیم صریح تراز (NSTextAlignment.left به جای NSTextAlignment.natural) به درستی مدیریت نمیکنند. طبق Google Internationalization Guide (2025)، برای برنامههای چندزبانه همیشه از natural alignment استفاده کنید و تورفتگیها را در هر دو طرح بررسی کنید.
// 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 فقط از طریق ویژگیهای سراسری فیلد متنی بدون در نظر گرفتن قالببندی متفاوت پاراگرافهای جداگانه. در UILabel یا TextView با چندین پاراگراف جدا شده با \n، ParagraphStyle به طور یکسان برای کل متن اعمال میشود، اگر از attributed-text با سبکهای مختلف برای هر پاراگراف استفاده نشود.
طبق UX Collective — Common Typography Bugs in Mobile Apps (2025)، مشکلات ParagraphStyle 18% از کل اشکالات تایپوگرافیک در برنامههای موبایل را تشکیل میدهند. بحرانیترین موارد زمانی است که paragraphSpacing نادرست منجر به همپوشانی متن در خطوط مختلف میشود یا زمانی که بومیسازیهای RTL با تورفتگیهای LTR نمایش داده میشوند. توصیه میشود برای هر پارامتر ParagraphStyle با رشتههای آزمایشی به زبانهای مختلف، تستهای واحد ایجاد کنید.
سوالات متداول
NSMutableParagraphStyle قالببندی پاراگراف (تراز، تورفتگی، فاصله بین خطوط) را مدیریت میکند، در حالی که TextStyle ویژگیهای کاراکتری (فونت، رنگ، اندازه) را مدیریت میکند. هر دو از طریق NSAttributedString اعمال میشوند. ParagraphStyle در سطح پاراگراف عمل میکند، TextStyle در سطح کاراکتر. چندین TextStyle میتوانند در یک پاراگراف باشند، اما ParagraphStyle — فقط یکی در هر پاراگراف.
در Jetpack Compose، تورفتگی خط اول از طریق TextIndent در داخل TextStyle تنظیم میشود: TextStyle(textIndent = TextIndent(firstLine = 16.sp)). مقدار باید بر حسب sp باشد تا با متن مقیاس شود. برای متن چندخطی، تورفتگی به هر خطی که پاراگراف جدیدی را شروع میکند (شخصیت \n) اعمال میشود. برای اولین پاراگراف، تورفتگی همیشه اعمال میشود.
در SwiftUI معادل مستقیمی برای paragraphSpacing وجود ندارد. برای فاصله بین پاراگرافها از AttributedString (iOS 15+) با NSMutableParagraphStyle و ویژگی paragraphSpacing استفاده کنید. یا پاراگرافها را از طریق Text("...\n\n...") جدا کنید و بین بلوکهای متنی .padding() اضافه کنید. برای سناریوهای تایپوگرافیک پیچیده از NSAttributedString از طریق UILabelRepresentable استفاده کنید.
TextView در Android ویژگی paragraphSpacing را ندارد — از طریق SpannableString با LeadingMarginSpan یا LineBackgroundSpan سفارشی تنظیم میشود. برای فاصله بین خطوط از setLineSpacing استفاده کنید، برای فاصله بین پاراگرافها — پاراگرافها را با \n اضافی جدا کنید یا از کانتینر ترکیبی از چندین TextView استفاده کنید.
ParagraphStyle صحیح دسترسی را بهبود میبخشد: paragraphSpacing کافی به کاربران با اختلالات شناختی کمک میکند تا مرزهای پاراگراف را بهتر تشخیص دهند. WCAG 2.2 جداسازی بصری پاراگرافها را توصیه میکند (تورفتگی یا فاصله). paragraphSpacing نباید کمتر از 1.5 × line-height باشد. برای بومیسازیهای RTL بررسی کنید که تورفتگیها به صورت آینهای نمایش داده میشوند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.