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-у — уместо тога користи се комбинација модификатора: .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-система и Jetpack Compose-а. У класичном View-систему, параметри пасуса се постављају кроз XML атрибуте TextView-а (android:textAlignment, android:lineSpacingExtra) или кроз SpannableString са имплементацијама интерфејса ParagraphStyle из пакета android.text.style.
// Android View систем: 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(
'Стилизовани пасус у 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 подржава аутоматско пребацивање кроз 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-текст са различитим стиловима за сваки пасус.
Према 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 креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође