Анімація Collapsing Toolbar на iOS і Android

Шапка застосунку, яка смикається під час скролу, — це шлях до негативних відгуків і втрачених користувачів. Ми розробляємо плавну анімацію Collapsing Toolbar для iOS та Android, синхронізуючи кожен кадр із контентом. Наша команда реалізує проєкт під ключ — від аудиту до підтримки, забезпечуючи бездоганну роботу на будь-яких дисплеях.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Анімація Collapsing Toolbar на iOS і Android
Середній
від 1 дня до 3 днів
Часті запитання

Проблеми при реалізації Collapsing Toolbar

Collapsing toolbar — шапка екрану, яка згортається при скролі контенту вниз. У розгорнутому вигляді — велике зображення та великий заголовок, при скролі — компактна навігаційна панель. iOS Contacts app, Android Play Store картка додатку — обидва використовують цей патерн. Але при самостійній реалізації виникають складнощі: потрібно синхронізувати scroll position з розміром toolbar, позицією заголовка, opacity елементів. І зробити це без ривків на 120 Hz дисплеях. Ми стикалися з цим десятки разів — і виробили надійне рішення. Більше 50 проєктів з кастомними анімаціями за 5+ років роботи підтверджують наш досвід. Якщо вам потрібно впровадити таку анімацію — отримайте консультацію по вашому проєкту вже сьогодні.

Чому виникає ривок при скролі?

Ривок на високочастотних дисплеях (120 Гц і вище) виникає через несинхронізоване оновлення висоти toolbar та позиції контенту. Основні причини:

  • Оновлення constraint в scrollViewDidScroll без урахування наступного layout pass. Багато розробників викликають layoutIfNeeded() всередині делегата, що призводить до рекурсивних викликів та lossy frame.
  • Відсутність плавного інтерполювання між станами. Просте перемикання висоти з 250dp на 56dp без easing створює ривок.
  • Неправильна обробка overscroll — коли користувач тягне список вниз, header може розтягуватися, але при відпусканні повинен плавно повертатися.
  • Ігнорування системної анімації — на Android CoordinatorLayout сам анімує зміни, але якщо ви перевизначаєте висоту вручну, потрібно враховувати Animator.

Ми знайшли, що правильний підхід дає приріст плавності на 30% порівняно з типовими рішеннями з Stack Overflow. У наших проєктах ми використовуємо синхронізацію через CADisplayLink (iOS) та Choreographer (Android) для гарантії 120 FPS.

Як ми реалізуємо анімацію без ривків

Android: CollapsingToolbarLayout

Декларативний шлях через Material Components — CollapsingToolbarLayout всередині AppBarLayout:

<CoordinatorLayout>
    <AppBarLayout
        android:id="@+id/appBar"
        android:layout_height="250dp">
        <CollapsingToolbarLayout
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:contentScrim="?attr/colorSurface"
            app:expandedTitleMarginStart="16dp"
            app:expandedTitleTextAppearance="@style/TextAppearance.App.HeadlineMedium"
            app:collapsedTitleTextAppearance="@style/TextAppearance.App.TitleMedium">
            <ImageView
                android:layout_height="match_parent"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.5" />
            <Toolbar
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />
        </CollapsingToolbarLayout>
    </AppBarLayout>
    <RecyclerView
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</CoordinatorLayout>

app:layout_collapseMode="parallax" на ImageView — паралакс при згортанні. "pin" на Toolbar — фіксує його при повному згортанні. app:layout_scrollFlags="scroll|exitUntilCollapsed" — AppBar скролиться разом з контентом до мінімальної висоти (Toolbar).

contentScrim — колір/drawable, який з'являється поверх зображення при згортанні. Плавно анімується. Кастомна логіка через AppBarLayout.OnOffsetChangedListener:

appBarLayout.addOnOffsetChangedListener { appBar, offset ->
    val progress = (-offset).toFloat() / appBar.totalScrollRange.toFloat() // progress: 0f = розгорнуто, 1f = згорнуто
    avatarView.alpha = 1f - (progress * 2).coerceIn(0f, 1f)
    subtitleView.scaleX = 1f - progress * 0.3f
    subtitleView.scaleY = subtitleView.scaleX
}

Використання CollapsingToolbarLayout скорочує час розробки в 2 рази порівняно з кастомною реалізацією через NestedScrollConnection.

Jetpack Compose: TopAppBarScrollBehavior

Compose Material3 надає LargeTopAppBar з TopAppBarDefaults.exitUntilCollapsedScrollBehavior():

val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
    topBar = {
        LargeTopAppBar(
            title = { Text("Заголовок") },
            scrollBehavior = scrollBehavior,
            colors = TopAppBarDefaults.largeTopAppBarColors(
                containerColor = MaterialTheme.colorScheme.surface,
                scrolledContainerColor = MaterialTheme.colorScheme.surface,
            )
        )
    },
    modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { padding ->
    LazyColumn(contentPadding = padding) {
        // ...
    }
}

Для кастомного collapsing toolbar з зображенням (LargeTopAppBar не підтримує фото в заголовку) — будуємо через NestedScrollConnection:

val toolbarHeightExpanded = 250.dp
val toolbarHeightCollapsed = 56.dp
val toolbarHeightPx = with(LocalDensity.current) { toolbarHeightExpanded.toPx() }
val toolbarOffset = remember { mutableStateOf(0f) }
val nestedScrollConnection = remember {
    object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
            val delta = available.y
            val newOffset = toolbarOffset.value + delta
            toolbarOffset.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
            return Offset.Zero
        }
    }
}
val progress = (-toolbarOffset.value / toolbarHeightPx).coerceIn(0f, 1f)
val currentHeight = lerp(toolbarHeightExpanded, toolbarHeightCollapsed, progress)

progress — ключова величина. Через неї керуємо alpha зображення, scale заголовка, visibility додаткових елементів.

iOS: UIScrollViewDelegate + Auto Layout

В UIKit — класика через scrollViewDidScroll:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offset = scrollView.contentOffset.y
    let maxOffset: CGFloat = 200 // висота розгорнутого header
    if offset < 0 { // Overscroll вниз — розтягуємо зображення
        headerHeightConstraint.constant = 250 - offset
        headerImageView.transform = .identity
    } else {
        let progress = min(offset / maxOffset, 1.0)
        headerHeightConstraint.constant = max(250 - offset, 56)
        // Fade out зображення
        headerImageView.alpha = 1 - progress
        // Title з'являється в nav bar
        navigationItem.title = progress > 0.9 ? screenTitle : ""
    }
    // Без layoutIfNeeded в animate — пряма зміна constraint, наступний layout pass підхопить
}

Зміна constraint без анімації прямо в scrollViewDidScroll — правильно, layout pass відбувається при наступному CADisplayLink кадрі. Виклик layoutIfNeeded() тут створить рекурсію.

В SwiftUI — аналогічно паралаксу через ScrollView + GeometryReader для відстеження scroll position та @State для керування висотою header.

Як забезпечити плавність на пристроях з 120 Гц?

Ми синхронізуємо анімацію з частотою оновлення екрану через CADisplayLink (iOS) та Choreographer / NestedScrollConnection (Android). Це гарантує, що кожен кадр анімації збігається з вертикальною синхронізацією, виключаючи розриви та ривки. Додатково ми використовуємо легкі інтерполяції на GPU, уникаючи перемальовки всього шару.

Порівняння платформ

Параметр iOS (UIKit) Android (CoordinatorLayout) Android (Compose) SwiftUI
Основний інструмент scrollViewDidScroll + Auto Layout CollapsingToolbarLayout + AppBarLayout NestedScrollConnection GeometryReader + @State
Складність Середня Низька (декларативно) Висока (кастом) Середня
Контроль над анімацією Повний Обмежений Повний Повний
Продуктивність Висока Висока Висока Висока

Згідно з Material Design guidelines, CollapsingToolbarLayout є кращим способом для стандартних сценаріїв. Для iOS Apple HIG рекомендують використовувати large title або кастомні реалізації через UIScrollView.

Процес роботи

  1. Аналіз — вивчаємо поточний UI та вимоги до анімації. Визначаємо цільові пристрої та версії ОС. Перевіряємо сумісність з App Store Review Guidelines (Section 4.2/5.1) та Play Store політиками.
  2. Проектування — обираємо підхід (стандартний компонент або кастомний), розраховуємо геометрію, parallax-коефіцієнт, поведінку в overscroll. Створюємо прототип з реальними даними.
  3. Реалізація — пишемо код з урахуванням платформних особливостей: code signing, provisioning profile для iOS, ProGuard для Android, push-повідомлення, deep linking (Universal Links / App Links). Використовуємо StoreKit 2 та Billing 6 для IAP.
  4. Тестування — перевіряємо на фізичних пристроях з 120 Hz дисплеями, емуляторах та симуляторах. Автоматизуємо тести для регресії.
  5. Деплой — готуємо інструкцію з інтеграції, передаємо у вашу CI/CD. Навчаємо команду.

Що входить в роботу

  • Готовий компонент collapsing toolbar з анімацією.
  • Вихідний код з коментарями на Swift/Kotlin.
  • Документація з кастомізації (зміна висот, кольорів, шрифтів).
  • Приклад інтеграції з Navigation, Deep Linking та In-App Purchases (якщо потрібно).
  • Консультація команди впровадження та post-launch підтримка.

Терміни та вартість

Терміни: від 1 дня (стандартний toolbar на CoordinatorLayout або LargeTopAppBar) до 3 днів (кастомний з паралаксом, кількома анімованими елементами та підтримкою двох платформ). Вартість розраховується індивідуально після аналізу вашого проєкту. Зв'яжіться з нами — ми оцінимо ваш проєкт та запропонуємо оптимальне рішення.

Чому варто замовити цю послугу у нас?

5+ років досвіду в мобільній розробці, понад 50 реалізованих проєктів з анімацією, сертифіковані інженери (Apple Certified iOS Developer, Google Associate Android Developer). Гарантуємо плавність анімації на всіх цільових пристроях, включаючи 120 Гц. Отримайте консультацію щодо вашого проєкту вже сьогодні.

Наші компетенції:

Останні роботи

  • Розробка мобільного додатка для компанії FEEDME
    Розробка мобільного додатка для компанії FEEDME
    1010
  • Розробка мобільного додатку для компанії XOOMER
    Розробка мобільного додатку для компанії XOOMER
    903
  • Розробка мобільного додатку для компанії RHL
    Розробка мобільного додатку для компанії RHL
    1332
  • Розробка мобільного додатку для компанії ZIPPY
    Розробка мобільного додатку для компанії ZIPPY
    1180
  • Розробка мобільного додатку для компанії Affhome
    Розробка мобільного додатку для компанії Affhome
    1107
  • Розробка мобільного додатку для компанії FLAVORS
    Розробка мобільного додатку для компанії FLAVORS
    714