Компоненты
Компоненты — общие строительные блоки темы (панель навигации, поле ввода, плеер и т.д.). Один компонент переиспользуется на разных экранах и настраивается централизованно через ChatComponents; в отдельных пользовательских сценариях стиль можно переопределить точечно.
Пример настройки:
// Создание компонентов с кастомными цветами
let components = ChatComponents(colors: myColors, typography: myTypography)
// Переопределение параметров общих для SDK компонентов
components.navigationBarStyle.titleTextStyle.color = .white
// Cancel-кнопка в search-баре — это UIKit-овый UISearchBar.cancelButton; из-за
// ограничений UISearchBar API применяется только `color.normal`.
// `tintColor` и другие свойства ButtonStyle для этого элемента игнорируются —
// см. design-system/known_issues.md.
components.searchBarStyle.cancelButtonStyle.color.normal = .white
// Создание темы из компонентов
let theme = ChatTheme(components: components)
или через конструктор build:
let components = ChatComponents.build { components in
components.searchBarStyle.cancelButtonStyle.color.normal = .black
components.loadingChatStyle.indicatorStyle.backgroundColor = .systemGray3
components.loadingChatStyle.indicatorStyle.cornerRadius = 20.0
components.audioPlayerStyle.playButtonStyle.image = ChatImage(system: "play.fill", tintColor: .red)
components.audioPlayerStyle.pauseButtonStyle.image = ChatImage(system: "pause.fill", tintColor: .green)
components.audioPlayerStyle.progressViewStyle.color = .black
components.audioPlayerStyle.progressViewStyle.backgroundColor = .yellow
}
Параметры конструктора (все опциональные, имеют значения по умолчанию):
images:ChatImages— изображения, используемые в теме.colors:ChatColors— цветовая палитра.typography:ChatTypography— настройки шрифтов.
Стили компонентов чата
| Название | Описание |
|---|---|
navigationBarStyle | Панель навигации: заголовок, кнопки, фон. |
searchBarStyle | Панель поиска внутри navigationBarStyle. |
loadingIndicatorStyle | Индикатор загрузки (спиннер). |
loadingChatStyle | Экран загрузки при открытии чата. |
chatPlaceholderStyle | Заглушка для пустого чата. |
errorPlaceholderStyle | Экран ошибки с кнопкой «Повторить». |
inputTextStyle | Текстовое поле ввода сообщения. |
inputSearchTextStyle | Текстовое поле в панели поиска. |
audioPlayerStyle | Плеер голосовых сообщений. |
inputViewStyle | Нижняя панель чата: поле ввода, кнопки отправки и прикрепления. |
chatMenuStyle | Меню выбора файла/фото/камеры. |
photoPickerStyle устарелphotoPickerStyle — deprecated, используйте chatMenuStyle.
Анатомия экранов
Скриншоты ниже сопоставляют визуальные зоны iOS-чата с полями ChatComponents, chatFlow и searchFlow. Снято с демо-приложения (светлая тема по умолчанию). Структура полей одинакова для любой темы.
Стиль задаётся глобально через components.X (все flow) или точечно через theme.flows.chatFlow.X / theme.flows.searchFlow.X (один экран). Подробнее о приоритете — в Дизайн-системе.
Главный экран чата

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Кнопка «Назад» в навигации | chatFlow.navigationBarStyle.backButtonColor | NavigationBarStyle |
| Заголовок чата (имя оператора) | chatFlow.navigationBarStyle.titleTextStyle | NavigationBarStyle, ChatTextStyle |
| Подзаголовок (роль оператора) | chatFlow.navigationBarStyle.subtitleTextStyle | NavigationBarStyle, ChatTextStyle |
| Иконка поиска в навигации | chatFlow.navigationBarStyle.searchButtonStyle | NavigationBarStyle, NavigationBarButtonStyle |
| Фон экрана чата | chatFlow.backgroundColor | ChatFlow |
| Дата-разделитель («сегодня») | chatFlow.systemMessages.dateMessageStyle | ChatSystemMessagesStyles, ChatTextStyle |
| Цвет пузыря исходящего сообщения | chatFlow.outcomeMessages.bubbleTintColor | ChatMessagesStyles |
| Текст исходящего сообщения | chatFlow.outcomeMessages.textMessageStyle.textStyle | TextChatMessageStyle, ChatTextStyle |
| Иконки статуса (отправлено / доставлено / прочитано) | ChatMessageStyle.pendingStatusImage / deliveredStatusImage / readStatusImage | ChatMessageStyle |
| Системное сообщение («Вам ответит…») | chatFlow.systemMessages | ChatSystemMessagesStyles |
| Кнопки быстрого ответа | См. QuickReplyStyle (Стили) | QuickReplyStyle |
| Поле ввода сообщения | chatFlow.inputViewStyle.inputTextStyle | ChatInputStyle, ChatInputTextStyle |
| Кнопка прикрепления (clip) | chatFlow.inputViewStyle.attachButtonStyle | ChatInputStyle, IconButtonStyle |
| Кнопка микрофона (в неактивном состоянии) | chatFlow.inputViewStyle.voiceButtonStyle | ChatInputStyle, IconButtonStyle |
| Кнопка отправки | chatFlow.inputViewStyle.sendButtonStyle | ChatInputStyle, IconButtonStyle |
| Кнопки быстрого ответа | chatFlow.quickRepliesStyle | QuickReplyStyle |
Контекстное меню сообщения

Меню появляется при длительном нажатии на сообщение; набор действий («Цитировать», «Копировать», «Удалить») определяется состоянием сообщения и серверной конфигурацией.
| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Выделенный пузырь сообщения | chatFlow.outcomeMessages / incomeMessages | ChatMessagesStyles |
| Действия меню («Цитировать» и т.п.) | Не настраивается через SDK — отрисовка через системный UIKit (UIMenu/UIMenuController) | — |
Поиск сообщений

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Поисковая строка | searchFlow.navigationBarStyle.searchBarStyle | SearchBarStyle |
| Текст и курсор в поле поиска | components.inputSearchTextStyle | ChatInputTextStyle |
| Кнопка «Отмена» | searchFlow.navigationBarStyle.searchBarStyle.cancelButtonStyle.color.normal | ChatButtonColor |
| Фон экрана поиска | searchFlow.backgroundColor | ChatUserFlow |
| Иллюстрация «ничего не найдено» | ChatImages.searchNotFoundPlaceholderImage | ChatImages |
| Текст «ничего не найдено» | searchFlow.notFoundTextStyle | ChatTextStyle |
Результаты поиска

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Подсветка совпадений в тексте | searchFlow.searchMessageStyle.matchTextStyle | SearchChatMessageStyle, ChatTextStyle |
| Стиль строки результата (имя, дата, превью) | searchFlow.searchMessageStyle | SearchChatMessageStyle |
| Кнопка очистки поля поиска (×) | searchFlow.navigationBarStyle.searchBarStyle | SearchBarStyle |
Пустое состояние чата

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Иллюстрация приветствия | ChatImages.emptyChatPlaceholderImage | ChatImages |
| Заголовок «Добро пожаловать…» | components.chatPlaceholderStyle.titleTextStyle | ChatPlaceholderStyle, ChatTextStyle |
| Подзаголовок (описание) | components.chatPlaceholderStyle.subtitleTextStyle | ChatPlaceholderStyle, ChatTextStyle |
Иллюстрация (через ChatPlaceholderStyle) | components.chatPlaceholderStyle.image | ChatPlaceholderStyle, ChatImage |
| Фон экрана | chatFlow.backgroundColor | ChatFlow |
Загрузка чата

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Индикатор-спиннер | components.loadingChatStyle.indicatorStyle | ChatLoadingStyle, LoadingIndicatorStyle |
| Текст «Загрузка» | components.loadingChatStyle.textStyle | ChatLoadingStyle, ChatTextStyle |
| Фон лоадера | components.loadingChatStyle.indicatorStyle.backgroundColor | LoadingIndicatorStyle |
Ошибка инициализации чата

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Иллюстрация ошибки (по умолчанию) | components.errorPlaceholderStyle.defaultErrorImage | ChatPlaceholderErrorStyle, ChatImage |
| Иллюстрация при ошибке сервера (HTTP 4xx/5xx) | components.errorPlaceholderStyle.serverErrorImage | ChatPlaceholderErrorStyle, ChatImage |
| Заголовок ошибки | components.errorPlaceholderStyle.titleTextStyle | ChatPlaceholderErrorStyle, ChatTextStyle |
| Подзаголовок ошибки | components.errorPlaceholderStyle.subtitleTextStyle | ChatPlaceholderErrorStyle, ChatTextStyle |
| Кнопка «Повторить» — цвет фона | components.errorPlaceholderStyle.repeatButtonStyle.color.normal | TextButtonStyle, ChatButtonColor |
| Кнопка «Повторить» — цвет текста | components.errorPlaceholderStyle.repeatButtonStyle.titleTextStyle.color | TextButtonStyle, ChatTextStyle |
Запись голосового сообщения

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Кнопка микрофона (триггер записи) | chatFlow.inputViewStyle.voiceButtonStyle | ChatInputStyle, IconButtonStyle |
| Стиль записи целиком (отмена, таймер, прогресс) | chatFlow.inputViewStyle.voiceRecordStyle | ChatInputStyle, ChatInputVoiceStyle |
| Кнопка отмены записи (×) — внутри voice-режима | chatFlow.inputViewStyle.voiceRecordStyle (см. Стили) | ChatInputVoiceStyle |
Завершение диалога и оценка

| Элемент | Путь в темах | Класс стиля |
|---|---|---|
| Аватар оператора у входящего сообщения | chatFlow.incomeMessages.showAvatar (вкл/выкл) | ChatMessagesStyles |
| Стиль системного сообщения «Диалог завершён…» | chatFlow.systemMessages.dateMessageStyle (для нейтрального системного текста) | ChatSystemMessagesStyles, ChatTextStyle |
| Заголовок приглашения оценить | chatFlow.systemMessages.surveyMessageStyle.questionTitleTextStyle | SurveyChatMessageStyle, ChatTextStyle |
| Подзаголовок приглашения | chatFlow.systemMessages.surveyMessageStyle.questionSubtitleTextStyle | SurveyChatMessageStyle, ChatTextStyle |
| Иконка звезды (выбранная / невыбранная) | chatFlow.systemMessages.surveyMessageStyle.voteIcon | SurveyChatMessageStyle, ChatSurveyIcon |
| Цвет звёзд (выбранные / невыбранные) | voteSelectedColor / voteUnselectedColor | SurveyChatMessageStyle, UIColor |
| Иконки лайк/дизлайк (для бинарного опроса) | voteLikeIcon / voteDislikeIcon | SurveyChatMessageStyle, ChatSurveyIcon |
Полный список свойств — в Стилях и в Сценариях (flows). Если поля нет в публичном API SDK, оно не настраивается — см. Известные ограничения.
Примеры кастомизации типичных элементов
Поле ввода текста
components.inputTextStyle.apply { style in
style.cursorColor = UIColor(named: "BrandColor") ?? .systemBlue // курсор
style.textStyle.color = .label // цвет текста
style.backgroundColor = .systemBackground
}
Или только в экране чата (не затрагивая поиск):
let customInput = ChatInputTextStyle.build(with: components) { style in
style.cursorColor = UIColor(named: "BrandColor") ?? .systemBlue
}
theme.flows.chatFlow.inputViewStyle.inputTextStyle = customInput
Навигационная панель
components.navigationBarStyle.apply { style in
style.titleTextStyle.font = .systemFont(ofSize: 17, weight: .semibold)
style.titleTextStyle.color = .white
style.backgroundColor = UIColor(named: "BrandColor") ?? .systemBlue
}
Поведение зависит от флага isConfigurable в NavigationBarStyle:
isConfigurable=true— кастомный заголовок: применяются все свойстваtitleTextStyle(font + color) иsubtitleTextStyle.isConfigurable=false— стандартный нав-бар UIKit: применяется толькоtitleTextStyle.color(черезtitleTextAttributes). ШрифтtitleTextStyle.fontи весьsubtitleTextStyleигнорируются.
См. также Известные ограничения → Параметры с условиями применения.
Цвет пузырей сообщений
// Входящие (от оператора)
theme.flows.chatFlow.incomeMessages.bubbleTintColor = UIColor(named: "BubbleIncoming") ?? .systemGray5
// Исходящие (от клиента)
theme.flows.chatFlow.outcomeMessages.bubbleTintColor = UIColor(named: "BrandColor") ?? .systemBlue
// Цвет текста в пузырях
theme.flows.chatFlow.incomeMessages.textMessageStyle.textStyle.color = .label
theme.flows.chatFlow.outcomeMessages.textMessageStyle.textStyle.color = .white
Свойство bubbleErrorColor в ChatMessagesStyles применяется только к исходящим сообщениям (от клиента). Входящие сообщения не отображают ошибку в цвете пузыря.
Кнопка отправки
let customInput = ChatInputStyle.build(with: components) { style in
// Цвет иконки задаётся в самом ChatImage, цвет фона/состояний — через color.
style.sendButtonStyle.image = ChatImage(system: "paperplane.fill", tintColor: .white)
style.sendButtonStyle.color.normal = UIColor(named: "BrandColor") ?? .systemBlue
style.sendButtonStyle.color.disabled = .systemGray3
}
theme.flows.chatFlow.inputViewStyle = customInput
Цвета кнопок (ChatButtonColor)
Задаёт цвет кнопки в трёх состояниях: normal, highlighted, disabled. Используется в ButtonStyle, IconButtonStyle, NavigationBarButtonStyle, TextButtonStyle.
let buttonColor = ChatButtonColor(
normal: .systemBlue, // обычное состояние
highlighted: .systemBlue.withAlphaComponent(0.7), // при нажатии
disabled: .systemGray3 // неактивная кнопка
)
Все три состояния обязательны. По умолчанию создаются из палитры ChatColors:
| Состояние | Цвет по умолчанию |
|---|---|
normal | colors.link |
highlighted | colors.linkLight |
disabled | colors.disabled |
Можно применить кастомные цвета либо при создании экземпляра, либо через свойства:
buttonColor.normal = UIColor(named: "BrandColor") ?? .systemBlue
Панель поиска
components.searchBarStyle.apply { style in
// Для cancel-кнопки в search-баре применяется только color.normal,
// tintColor самого SearchBarStyle и другие свойства ButtonStyle для
// этого элемента игнорируются (см. known_issues.md).
style.cancelButtonStyle.color.normal = .white
// Курсор и текст поля поиска настраиваются через inputSearchTextStyle
// (см. секцию «Поисковое поле ввода» ниже).
}
Индикатор загрузки
components.loadingIndicatorStyle.apply { style in
style.indicatorColor = UIColor(named: "BrandColor") ?? .systemBlue
}
Экран загрузки чата
components.loadingChatStyle.apply { style in
style.indicatorStyle.backgroundColor = .systemGray6
style.indicatorStyle.cornerRadius = 16.0
}
Пустое состояние чата
// Текст при пустом чате
components.chatPlaceholderStyle.apply { style in
style.titleTextStyle.color = .secondaryLabel
style.titleTextStyle.font = .systemFont(ofSize: 15, weight: .regular)
}
// Заменить иллюстрацию (через токен изображений)
let images = ChatImages()
images.emptyChatPlaceholderImage = ChatImage(named: "EmptyChat", bundle: .main)
Ошибка загрузки
components.errorPlaceholderStyle.apply { style in
style.titleTextStyle.color = .label
// Цвет фона кнопки «Повторить» — через ChatButtonColor (state-aware).
// Тип repeatButtonStyle — TextButtonStyle, и UI читает color.normal,
// а не tintColor.
style.repeatButtonStyle.color.normal = UIColor(named: "BrandColor") ?? .systemBlue
style.repeatButtonStyle.titleTextStyle.color = .white
}
// Заменить иллюстрацию ошибки (через токен)
images.errorPlaceholderImage = ChatImage(named: "ErrorIllustration", bundle: .main)
Аудиоплеер
components.audioPlayerStyle.apply { style in
style.playButtonStyle.image = ChatImage(system: "play.fill", tintColor: UIColor(named: "BrandColor") ?? .systemBlue)
style.pauseButtonStyle.image = ChatImage(system: "pause.fill", tintColor: UIColor(named: "BrandColor") ?? .systemBlue)
style.progressViewStyle.color = UIColor(named: "BrandColor") ?? .systemBlue
style.progressViewStyle.backgroundColor = .systemGray5
}
Поисковое поле ввода
components.inputSearchTextStyle.apply { style in
style.cursorColor = UIColor(named: "BrandColor") ?? .systemBlue
style.textStyle.color = .label
}
Соответствие Figma и SDK
На странице Components в Figma компоненты организованы в 8 секций. Ниже приведено соответствие между секциями Figma и стилями SDK:
Navigation (Figma)


| Компонент Figma | Варианты | Стиль SDK |
|---|---|---|
| Top Navigation | iOS/Android × Light/Dark | navigationBarStyle (NavigationBarStyle) |
| Bottom Input | Empty, Filled, Voice Record, Voice Recorded, Search Navigation | inputViewStyle (ChatInputStyle) |
| Cite Bottom | Message / Photo / Docs × Light/Dark | chatFlow.inputViewStyle.quoteStyle (ChatInputQuoteStyle) |
| Scroll Button | Light / Dark | chatFlow.scrollToTopUnreadMessagesButtonStyle и chatFlow.scrollToBottomUnreadMessagesButtonStyle (ScrollToMessageButtonStyle) |
| Badge | Single / Numerous × Light/Dark | Визуальный стиль бейджа не настраивается публично в SDK; количество непрочитанных доступно через getUnreadMessagesCount и делегат |
Status (Figma)

| Компонент Figma | Варианты | Стиль SDK |
|---|---|---|
| Message status | Прочитано, Доставлено, Доставляется, Ошибка, Отредактировано (Client/Agent, с подложкой/без) | ChatMessageStyle: readStatusImage, deliveredStatusImage, pendingStatusImage, editedStatusImage, errorStatusColor |
Button (Figma)

| Тип кнопки (Figma) | Состояния | Стиль SDK |
|---|---|---|
| Bot | Normal, Active, Pressed, Disable, Loading, Error | QuickReplyStyle |
| Text | Normal, Active, Pressed, Disable, Loading, Error | TextButtonStyle |
| Icon | Normal, Active, Pressed, Disable, Loading, Error | IconButtonStyle |
| Image | Normal, Active, Pressed, Disable, Loading, Error | ButtonStyle |
| Selection | Normal, Pressed, Disable | QuickReplyStyle |
Bubbles (Figma)

| Компонент Figma | Варианты | Стиль SDK |
|---|---|---|
| Message Base (Agent) | Normal, MultiContent, Deleted, Search agent, Error | chatFlow.incomeMessages (ChatMessagesStyles) |
| Message Base (Client) | Normal, MultiContent, Error | chatFlow.outcomeMessages (ChatMessagesStyles) |
| Системные сообщения | Snooze, дата, опрос, подключение оператора | chatFlow.systemMessages (ChatSystemMessagesStyles) |
Анатомия баббла в Figma: Avatar → Tail → Content (текст/медиа) → Status. Настраивается через:
ChatMessagesStyles.showAvatar— показывать аватарChatMessageStyle.messageBubbleEndMargin— отступ от края экранаChatMessageStyle.containerLeftOffset/containerRightOffset— отступы контейнера
Figma-состояния → SDK-свойства
| Figma состояние | Описание | Свойство SDK |
|---|---|---|
| Normal | Обычное сообщение | textMessageStyle, imageMessageStyle, bubbleTintColor |
| Typing | Индикатор «печатает» | Стиль публично не настраивается |
| Snooze | Оператор временно недоступен | chatFlow.systemMessages.scheduleMessageStyle (ScheduleChatMessageStyle) |
| Search agent | Результат поиска с выделением | searchFlow.searchMessageStyle (SearchChatMessageStyle) |
| Error | Ошибка отправки | ChatMessageStyle.errorStatusColor, errorTextStyle, errorInfoButton |
| Deleted | Удалённое сообщение | ChatMessagesStyles.deletedTextStyle (ChatTextStyle) |
Input Field (Figma)
| Компонент Figma | Варианты | Стиль SDK |
|---|---|---|
| Input field | Пустое, частично заполненное, полностью заполненное | inputTextStyle (ChatInputTextStyle) |
OS UI (Figma)
| Компонент Figma | Описание | Стиль SDK |
|---|---|---|
| iOS Select File | Системный выбор файла | chatMenuStyle (ChatMenuStyle) |
| Gallery | Галерея фотографий | chatMenuStyle |
| Select Image | Выбор изображения | chatMenuStyle |
Content (Figma)
| Компонент Figma | Описание | Стиль SDK |
|---|---|---|
| Content | Типы контента в сообщениях: текст, картинка, файл, аудио, опрос, цитата, OG-превью и др. | TextChatMessageStyle, ImageChatMessageStyle, FileChatMessageStyle, AudioChatMessageStyle, SurveyChatMessageStyle, QuoteStyle, OpenGraphViewStyle |
Illustration (Figma)
| Компонент Figma | Варианты | Стиль SDK |
|---|---|---|
| Illustration | Приветствие, Поиск, Ошибка и др. | emptyChatPlaceholderImage, errorPlaceholderImage, searchNotFoundPlaceholderImage (в ChatImages) |
Меню вложений (chatMenuStyle)
chatMenuStyle управляет внешним видом меню выбора файлов, фотографий и камеры — всплывающего листа, который появляется при нажатии на кнопку прикрепления.
Доступные свойства
Меню реализовано через системный UIAlertController, поэтому визуально применяются только три свойства:
| Свойство | Тип | Описание | Значение по умолчанию |
|---|---|---|---|
backgroundColor | UIColor | Фон листа меню | colors.background |
titleTextStyle.color | UIColor | Цвет текста и иконок кнопок (пробрасывается в alert.view.tintColor) | colors.link |
maximumImagesCount | UInt8 | Максимум выбираемых изображений | 10 |
tintColor, cornerRadius, titleTextStyle.fontЭти поля присутствуют на ChatMenuStyle (унаследованы от ChatStyle / ChatTextStyle), но из-за системной реализации UIAlertController визуально игнорируются. Цвет задавайте через titleTextStyle.color, скруглением управляет система.
Пример настройки
// Глобально через ChatComponents (применяется ко всем экранам)
components.chatMenuStyle.apply { style in
style.backgroundColor = UIColor(named: "BackgroundPrimary") ?? .white
style.titleTextStyle.color = UIColor(named: "BrandColor") ?? .systemBlue
}
Или через build для точечного создания нового экземпляра:
let menuStyle = ChatMenuStyle.build(with: components) { style in
style.backgroundColor = .white
style.titleTextStyle.color = .systemBlue
style.maximumImagesCount = 5
}
components.chatMenuStyle = menuStyle
Только для экрана чата
Если нужно изменить стиль меню только в чате (не затрагивая другие экраны):
// Доступ через chatFlow (свойство называется addFileMenuStyle)
theme.flows.chatFlow.addFileMenuStyle.apply { style in
style.backgroundColor = UIColor(named: "BackgroundPrimary") ?? .white
style.titleTextStyle.color = UIColor(named: "BrandColor") ?? .systemBlue
}
chatMenuStyleПри создании ChatFlow свойство addFileMenuStyle инициализируется ссылкой на тот же экземпляр, что хранится в components.chatMenuStyle на момент инициализации темы. После этого они независимы: если позже выполнить components.chatMenuStyle = newStyle, на chatFlow.addFileMenuStyle это не повлияет — там останется прежний объект.
Чтобы изменить меню только в chatFlow без глобального эффекта, создайте новый экземпляр через build и присвойте его напрямую: theme.flows.chatFlow.addFileMenuStyle = myMenuStyle.