ParagraphStyle — este un set de parametri de formatare care definește aspectul vizual al unui paragraf de text. În dezvoltarea mobilă, ParagraphStyle include alinierea (left, center, right, justified), spațiile între rânduri, indentarea primei linii, spațiile între paragrafe și direcția textului. Conform Material Design Typography Guidelines (2025), utilizarea consecventă a ParagraphStyle pe toate ecranele aplicației asigură un ritm tipografic unitar și reduce zgomotul vizual la comutarea între secțiuni.
Principalele
ParagraphStyle (stilul paragrafului) — este un ansamblu de reguli de formatare aplicate unuia sau mai multor paragrafe de text. Spre deosebire de stilurile de caracter (bold, italic, font-size) care acționează la nivelul caracterelor individuale, ParagraphStyle gestionează macroproprietăți: amplasarea textului în container, distanțele între rânduri și paragrafe, direcția de scriere și regulile de întrerupere.
Din punct de vedere istoric, conceptul ParagraphStyle provine din sistemele de publicare desktop (QuarkXPress, PageMaker, InDesign), unde fiecare paragraf avea propriul set de parametri. În dezvoltarea mobilă, această abordare s-a transformat în API-uri programatice: NSMutableParagraphStyle pe iOS, clasele ParagraphStyle și LineBreakStyle în Android, precum și parametri precum textAlign și textHeightBehavior în Flutter. Toate rezolvă aceeași sarcină — gestionarea formatării paragrafelor prin cod, fără a fi legate de un motor text specific.
Conform Bringhurst — The Elements of Typographic Style (2024), configurarea corectă a ParagraphStyle stă la baza culturii tipografice a interfeței. Dacă fontul și dimensiunea răspund de „vocea" textului, stilul paragrafului răspunde de „respirația" acestuia: ritmul, pauzele și accentele. În interfețele mobile, unde spațiul este limitat, un ParagraphStyle adecvat permite includerea mai multor informații fără pierderea lizibilității.
ParagraphStyle include mai multe grupuri de proprietăți, fiecare răspunzând pentru un aspect specific al formatării. Să examinăm parametrii cheie disponibili în API-urile mobile. Alinierea (alignment) — parametrul principal care determină poziția orizontală a textului. Pe iOS este disponibil NSTextAlignment, pe Android — Layout.Alignment, în Flutter — TextAlign.
| Proprietate | iOS (NSMutableParagraphStyle) | Android (Compose) | Flutter (TextStyle) |
|---|---|---|---|
| Aliniere | alignment | textAlign | textAlign |
| Indentare stângă | headIndent | textIndent (în Compose) | — |
| Indentare dreaptă | tailIndent | — | — |
| Indentare prima linie | firstLineHeadIndent | textIndent (firstLine) | — |
| Spațiu între paragrafe | paragraphSpacing | paragraphStyle (lineHeight) | — |
| Spațiu înainte de paragraf | paragraphSpacingBefore | — | — |
| Direcția textului | baseWritingDirection | TextDirection (Compose) | textDirection |
| Mod de întrerupere rând | lineBreakMode | overflow (TextStyle) | overflow (TextStyle) |
Este important de înțeles că nu toate proprietățile sunt disponibile pe toate platformele în aceeași măsură. De exemplu, indentarea primei linii (firstLineHeadIndent) este pe deplin suportată doar în iOS prin NSMutableParagraphStyle. În Android, în acest scop se utilizează SpannableString cu LeadingMarginSpan, iar în Flutter — RichText cu TextSpan personalizat și padding. În dezvoltarea cross-platform (Flutter, React Native) unele proprietăți ale ParagraphStyle trebuie emulate prin containere sau Spacer.
În iOS, instrumentul principal pentru lucrul cu stilul paragrafului este clasa NSMutableParagraphStyle — moștenită din NSParagraphStyle. Aceasta oferă un set complet de proprietăți pentru gestionarea formatării paragrafelor și se aplică prin NSAttributedString. NSMutableParagraphStyle este utilizat nu doar în UILabel și UITextView, ci și în Core Text prin CTParagraphStyle.
// iOS: configurare completă ParagraphStyle
let paragraphStyle = NSMutableParagraphStyle()
// Aliniere
paragraphStyle.alignment = .justified // justified
// Indentare
paragraphStyle.headIndent = 16 // indentare stângă
paragraphStyle.tailIndent = -16 // indentare dreaptă (negativă)
paragraphStyle.firstLineHeadIndent = 32 // indentare prima linie
// Spațiu între paragrafe
paragraphStyle.paragraphSpacing = 12 // după paragraf
paragraphStyle.paragraphSpacingBefore = 8 // înainte de paragraf
// Înălțime rând
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
)
În SwiftUI nu există un echivalent direct al NSMutableParagraphStyle — în schimb se utilizează o combinație de modificatori: .multilineTextAlignment() pentru aliniere, .lineSpacing() pentru spațiu între rânduri. Pentru indentarea primei linii și spațiile între paragrafe în SwiftUI trebuie să se folosească Text + padding sau AttributedString (disponibil din iOS 15+).
// SwiftUI: AttributedString cu 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)
Conform Apple — Text Programming Guide (2025), NSMutableParagraphStyle suportă până la 14 parametri diferiți de formatare. Este important de reținut că tailIndent se setează cu valoare negativă față de lățimea containerului, iar headIndent — cu valoare pozitivă. Acest comportament neevident duce adesea la erori la dezvoltatorii iOS începători.
În Android, abordarea față de ParagraphStyle diferă între View-system și Jetpack Compose. În View-system clasic, parametrii paragrafului se setează prin atributele XML ale TextView (android:textAlignment, android:lineSpacingExtra) sau prin SpannableString cu implementări ale interfeței ParagraphStyle din pachetul android.text.style.
// Android View system: ParagraphStyle prin SpannableString
val text = "First paragraph with settings.\n\nSecond paragraph."
val spannable = SpannableString(text)
// Aliniere justified
spannable.setSpan(
AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
0, text.length,
Spannable.SPAN_PARAGRAPH
)
// Indentare prima linie prin LeadingMarginSpan
spannable.setSpan(
LeadingMarginSpan.Standard(0, 20),
0, text.indexOf('\n'),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
textView.text = spannable
În Jetpack Compose, ParagraphStyle este gestionat prin parametrii TextStyle. Pentru aliniere se folosește textAlign, pentru spațiu între rânduri — lineHeight (în sp sau em), pentru indentarea primei linii — textIndent. Compose suportă de asemenea LineBreak pentru gestionarea fină a întreruperilor (Simple, Heading, Paragraph), util în special pentru text multilingv.
// Jetpack Compose: ParagraphStyle prin TextStyle
Text(
text = "Primul paragraf cu stil personalizat.\n\nAl doilea paragraf cu indentare.",
style = TextStyle(
textAlign = TextAlign.Justify,
lineHeight = 24.sp,
textIndent = TextIndent(firstLine = 16.sp),
lineBreak = LineBreak.Paragraph
)
)
Conform Android Developers — Compose ParagraphStyle (2025), când se utilizează TextIndent în Compose este important să se seteze firstLine în sp (nu în dp), deoarece indentarea primei linii trebuie să se scaleze împreună cu textul. Dacă indentarea se setează în dp, la mărirea dimensiunii fontului indentarea primei linii va deveni vizual mai mică, încălcând proporțiile tipografice.
În Flutter, paradigma ParagraphStyle nu este separată într-o clasă dedicată — în schimb se utilizează o combinație de parametri TextStyle și clase suplimentare. Alinierea se setează prin TextAlign, spațiile între rânduri — prin height (fără unitate), iar TextHeightBehavior gestionează indentările vizibile ale primei și ultimei linii.
// Flutter: ParagraphStyle prin parametrii Text
Text(
'Paragraf stilizat în Flutter.\n\nAl doilea paragraf cu indentări.',
style: TextStyle(
fontSize: 16,
height: 1.5,
leadingDistribution: TextLeadingDistribution.proportional,
),
textAlign: TextAlign.justify,
textHeightBehavior: TextHeightBehavior(
applyHeightToFirstAscent: true,
applyHeightToLastDescent: true,
),
)
Pentru scenarii mai complexe, Flutter oferă utilizarea RichText cu TextSpan și WidgetSpan personalizate. Aceasta permite combinarea textului cu widgeturi inline (icoane, imagini) într-un singur paragraf, păstrând reguli unitare de formatare. Totuși, această abordare necesită gestionarea manuală a întreruperilor și nu suportă așezarea automată a întreruperilor.
// Flutter: RichText cu formatare personalizată a paragrafului
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.'),
],
),
)
Conform Flutter Documentation — Text Widget (2025), TextHeightBehavior este parametrul cheie pentru afișarea corectă a ParagraphStyle în Flutter. Dacă applyHeightToFirstAscent este setat pe false, indentarea superioară a primei linii este ignorată, iar textul „se lipește" de marginea superioară a containerului. Acest lucru este util pentru titluri, dar nedorit pentru textul principal.
ParagraphStyle trebuie să țină cont de direcția de scriere pentru fiecare localizare. În aplicațiile mobile cu suport pentru arabă, ebraică sau urdu (scriere de la dreapta la stânga, RTL), indentările și alinierea trebuie să se schimbe în oglindă. iOS suportă comutarea automată prin baseWritingDirection = .natural, Android — prin TextDirection, Flutter — prin textDirection.
Configurarea alinierii pentru localizările RTL necesită o atenție deosebită. Dacă în modul LTR headIndent setează indentarea stângă, în RTL aceasta devine automat dreaptă. Cu toate acestea, nu toate implementările gestionează corect această tranziție la setarea explicită a alinierii (NSTextAlignment.left în loc de NSTextAlignment.natural). Conform Google Internationalization Guide (2025), pentru aplicații multilingve folosește întotdeauna natural alignment și verifică indentările în ambele configurații.
// Android: suport RTL în ParagraphStyle
val textView = TextView(context)
// Activează suportul automat RTL
textView.textDirection = View.TEXT_DIRECTION_LOCALE
textView.textAlignment = View.TEXT_ALIGNMENT_TEXT_START
// Spannable cu suport RTL
val spannable = SpannableString(arabicText)
spannable.setSpan(
AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
0, arabicText.length,
Spannable.SPAN_PARAGRAPH
)
Particularitățile regionale includ de asemenea reguli diferite pentru indentarea primei linii. În tipografia europeană, indentarea primei linii (firstLineHeadIndent) este standardul pentru evidențierea paragrafelor. În tipografia japoneză și chineză, indentarea primei linii este de asemenea utilizată, dar dimensiunea sa poate diferi (de obicei 1 em față de 1.5 em în tradiția europeană). În tipografia arabă, indentarea primei linii este rară, paragrafele fiind mai des separate prin spațiu vertical.
Cea mai frecventă greșeală — utilizarea ParagraphStyle doar prin atributele globale ale câmpului text fără a ține cont de formatarea diferită a paragrafelor individuale. În UILabel sau TextView cu mai multe paragrafe separate prin \n, ParagraphStyle se aplică întregului text în mod egal, dacă nu se utilizează attributed-text cu stiluri diferite pentru fiecare paragraf.
Conform UX Collective — Common Typography Bugs in Mobile Apps (2025), problemele cu ParagraphStyle constituie 18% din toate bug-urile tipografice în aplicațiile mobile. Cele mai critice sunt cazurile când paragraphSpacing incorect duce la suprapunerea textului pe rânduri diferite sau când localizările RTL se afișează cu indentări LTR. Se recomandă crearea de teste unitare pentru fiecare parametru ParagraphStyle cu șiruri de test în diferite limbi.
Întrebări frecvente
NSMutableParagraphStyle gestionează formatarea paragrafelor (aliniere, indentări, spații între rânduri), iar TextStyle — atributele de caracter (font, culoare, dimensiune). Ambele se aplică prin NSAttributedString. ParagraphStyle acționează la nivel de paragraf, TextStyle — la nivel de caractere. Mai multe TextStyle pot fi într-un paragraf, dar ParagraphStyle — doar unul pe paragraf.
În Jetpack Compose, indentarea primei linii se setează prin TextIndent în interiorul TextStyle: TextStyle(textIndent = TextIndent(firstLine = 16.sp)). Valoarea trebuie să fie în sp pentru a se scala cu textul. Pentru textul pe mai multe rânduri, indentarea se aplică fiecărui rând care începe un nou paragraf (caracterul \n). Pentru primul paragraf, indentarea se aplică întotdeauna.
În SwiftUI nu există un echivalent direct pentru paragraphSpacing. Pentru spațiul între paragrafe folosește AttributedString (iOS 15+) cu NSMutableParagraphStyle și proprietatea paragraphSpacing. Sau separă paragrafele prin Text("...\n\n...") și adaugă .padding() între blocurile de text. Pentru scenarii tipografice complexe, folosește NSAttributedString prin UILabelRepresentable.
TextView în Android nu are proprietatea paragraphSpacing — aceasta se setează prin SpannableString cu LeadingMarginSpan sau prin LineBackgroundSpan personalizat. Pentru spațiul între rânduri folosește setLineSpacing, pentru spațiul între paragrafe — separă paragrafele cu \n suplimentar sau folosește un container compozit din mai multe TextView.
Un ParagraphStyle corect îmbunătățește accesibilitatea: un paragraphSpacing adecvat ajută utilizatorii cu dizabilități cognitive să distingă mai bine limitele paragrafelor. WCAG 2.2 recomandă separarea vizuală a paragrafelor (indentare sau spațiu). paragraphSpacing nu trebuie să fie mai mic de 1.5 × line-height. Pentru localizările RTL, verifică dacă indentările se afișează în oglindă.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și