ParagraphStyle — ключевые понятия, свойства и применение

Автор: IT Sectr Опубликовано: 2026-07-25 Время чтения: 10 мин

ParagraphStyle — это набор параметров форматирования, определяющий визуальное оформление абзаца текста. В мобильной разработке ParagraphStyle включает выравнивание (left, center, right, justified), межстрочные интервалы, отступы первой строки, интервалы между абзацами и направление текста. По данным Material Design Typography Guidelines (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), где каждый абзац имел свой набор параметров. В мобильной разработке этот подход трансформировался в программные API: NSMutableParagraphStyle на iOS, классы ParagraphStyle и LineBreakStyle в Android, а также параметры вроде textAlign и textHeightBehavior в Flutter. Все они решают одну задачу — управление абзацным форматированием через код, без привязки к конкретному текстовому движку.

По данным Bringhurst — The Elements of Typographic Style (2024), грамотная настройка ParagraphStyle — основа типографической культуры интерфейса. Если гарнитура и кегль отвечают за «голос» текста, то стиль абзаца — за его «дыхание»: ритм, паузы и акценты. В мобильных интерфейсах, где пространство ограничено, правильный ParagraphStyle позволяет вместить больше информации без потери читаемости.

Основные свойства ParagraphStyle

ParagraphStyle включает несколько групп свойств, каждая из которых отвечает за определённый аспект форматирования. Рассмотрим ключевые параметры, доступные в мобильных API. Выравнивание (alignment) — основной параметр, определяющий положение текста по горизонтали. На 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
Line break modelineBreakModeoverflow (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: full ParagraphStyle configuration
let paragraphStyle = NSMutableParagraphStyle()

// Alignment
paragraphStyle.alignment = .justified     // justified

// Indentation
paragraphStyle.headIndent = 16          // left indent
paragraphStyle.tailIndent = -16         // right indent (negative)
paragraphStyle.firstLineHeadIndent = 32 // first line indent

// Paragraph spacing
paragraphStyle.paragraphSpacing = 12    // after paragraph
paragraphStyle.paragraphSpacingBefore = 8 // before paragraph

// Line height
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 через attributes.

swift
// SwiftUI: AttributedString with 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 system: ParagraphStyle via SpannableString
val text = "First paragraph with settings.\n\nSecond paragraph."
val spannable = SpannableString(text)

// Justified alignment
spannable.setSpan(
    AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
    0, text.length,
    Spannable.SPAN_PARAGRAPH
)

// First line indent via 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 via TextStyle
Text(
    text = "First paragraph with custom style.\n\nSecond paragraph with indent.",
    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 (unitless), а TextHeightBehavior управляет видимыми отступами первой и последней строки.

dart
// Flutter: ParagraphStyle via Text parameters
Text(
    'Styled paragraph in Flutter.\n\nSecond paragraph with indents.',
    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 with custom paragraph formatting
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-локалей требует особого внимания. Если в LTR-режиме headIndent задаёт левый отступ, то в RTL он автоматически становится правым. Однако не все реализации корректно обрабатывают этот переход при явном задании выравнивания (NSTextAlignment.left вместо NSTextAlignment.natural). По данным Google Internationalization Guide (2025), для многоязычных приложений всегда используй natural alignment и проверяй отступы в обеих раскладках.

kotlin
// Android: RTL support in ParagraphStyle
val textView = TextView(context)

// Enable automatic RTL support
textView.textDirection = View.TEXT_DIRECTION_LOCALE
textView.textAlignment = View.TEXT_ALIGNMENT_TEXT_START

// Spannable with RTL support
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 с несколькими абзацами, разделёнными \n, ParagraphStyle применяется ко всему тексту одинаково, если не использовать attributed-текст с разными стилями для каждого абзаца.

  • Игнорирование paragraphSpacingBefore — многие разработчики задают paragraphSpacing, но забывают про paragraphSpacingBefore. Для первого абзаца в блоке paragraphSpacingBefore позволяет задать отступ сверху, не создавая дополнительный контейнер. Это особенно полезно для списков с заголовками.
  • Жёсткое выравнивание (left/right) вместо natural — если приложение поддерживает RTL-локали, использование NSTextAlignment.left или TextAlign.Start вместо natural приводит к некорректному отображению в арабской и ивритской локалях.
  • Отсутствие проверки переносов (line break mode) — при узких контейнерах (списки, сайдбары) неправильный lineBreakMode приводит к обрезанию слов. Используй byWordWrapping и проверяй длинные слова в контексте конкретной локали.
  • Смешивание Spannable с разными ParagraphStyle — при установке Spannable с SPAN_PARAGRAPH важно правильно рассчитать границы абзацев. Несовпадение начальной/конечной позиции с фактическими границами абзаца приводит к игнорированию стиля.
  • Забытый baseline alignment — при использовании ParagraphStyle с фиксированным lineHeight (minimumLineHeight) убедись, что выравнивание текста по baseline включено. В iOS для этого используется label.baselineAdjustment = .alignBaselines.

По данным UX Collective — Common Typography Bugs in Mobile Apps (2025), проблемы с ParagraphStyle составляют 18% всех типографических багов в мобильных приложениях. Наиболее критичны случаи, когда неправильный paragraphSpacing приводит к перекрытию текста на разных строках или когда RTL-локали отображаются с LTR-отступами. Рекомендуется создавать unit-тесты для каждого параметра ParagraphStyle с тестовыми строками на разных языках.

Часто задаваемые вопросы

Чем ParagraphStyle отличается от TextStyle в iOS?

NSMutableParagraphStyle управляет абзацным форматированием (выравнивание, отступы, межстрочные интервалы), а TextStyle — символьными атрибутами (шрифт, цвет, кегль). Оба применяются через NSAttributedString. ParagraphStyle действует на уровне абзаца, TextStyle — на уровне символов. Несколько TextStyle могут быть в одном абзаце, но ParagraphStyle — только один на абзац.

Как задать красную строку в Jetpack Compose?

В Jetpack Compose отступ первой строки задаётся через TextIndent внутри TextStyle: TextStyle(textIndent = TextIndent(firstLine = 16.sp)). Значение должно быть в sp, чтобы масштабироваться с текстом. Для многострочного текста отступ применяется к каждой строке, начинающей новый абзац (символ \n). Для первого абзаца отступ применяется всегда.

Как настроить интервал между абзацами в SwiftUI?

В SwiftUI нет прямого аналога paragraphSpacing. Для интервала между абзацами используй AttributedString (iOS 15+) с NSMutableParagraphStyle и свойством paragraphSpacing. Либо разделяй абзацы через Text("...\n\n...") и добавляй .padding() между текстовыми блоками. Для сложных типографических сценариев используй NSAttributedString через UILabelRepresentable.

Почему paragraphSpacing не работает в Android TextView?

TextView в Android не имеет свойства paragraphSpacing — оно задаётся через SpannableString с LeadingMarginSpan или через кастомный LineBackgroundSpan. Для межстрочного интервала используй setLineSpacing, для межбзацного — разделяй абзацы дополнительным \n или используй составной контейнер из нескольких 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 для фиксации высоты строк.
  • Локализация — используй natural alignment для поддержки RTL-локалей; проверяй отступы в обеих раскладках.
  • Типичные ошибки — жёсткое выравнивание вместо natural, отсутствие учёта paragraphSpacingBefore, неверный lineBreakMode для узких контейнеров.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также