Shadow: суть, створення тіней у мобільних додатках

Автор: IT Sectr Опубліковано: 2026-02-28 Час читання: 9 хв

Shadow (тінь) — візуальний ефект у мобільних інтерфейсах, який створює ілюзію глибини та ієрархії між елементами. Тіні показують, який елемент знаходиться вище в z-площині і, отже, є більш важливим або інтерактивним. Згідно з Material Design Guidelines (Google, 2026), системні тіні використовують п'ять ключових висот (elevation): 0 dp (плоский), 1–4 dp (кнопки), 6–12 dp (картки), 16–24 dp (діалоги), 24+ dp (модальні вікна). Докладніше про висоту елементів читайте в статті про Elevation.

Головне

  • Тінь (shadow) — візуальний ефект, що створює глибину за рахунок затемнення області під елементом з розмиттям по краях.
  • На iOS тіні задаються через властивості CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • В Android тіні керуються через elevation (Material Design) або кастомне відтворення через OutlineProvider.
  • Jetpack Compose підтримує тіні через Modifier.shadow() та elevation у Card/Surface.
  • У Flutter тіні реалізуються через BoxShadow у BoxDecoration або через PhysicalModel.

Що таке тінь в UI?

Тінь (shadow) у користувацьких інтерфейсах — це візуальний ефект, що імітує освітлення та глибину. Тінь створюється шляхом відтворення темної розмитої копії елемента, зміщеної відносно оригіналу. У мобільному дизайні тіні виконують три функції: показують ієрархію (вищий елемент — довша тінь), сигналізують про інтерактивність (кнопки з тінню виглядають натисканими), відокремлюють контент від фону (картки з тінню читаються як окремі блоки).

У Material Design Google тіні нерозривно пов'язані з концепцією elevation (висоти). Кожен елемент займає певну висоту в z-координаті, і тінь — це проекція освітлення зверху. Human Interface Guidelines Apple не приписують строгу систему висот, але рекомендують використовувати тіні для створення візуальної ієрархії та глибини в інтерфейсах.

За даними дослідження Nielsen Norman Group (2024), правильно розставлені тіні скорочують час пошуку інтерактивного елемента на 24%. Тіні також допомагають користувачам з порушеннями зору розрізняти межі елементів. Однак надмірне використання тіней (3+ рівні на екрані) збільшує когнітивне навантаження та погіршує сприйняття.

Тіні на iOS: CALayer та SwiftUI

На iOS тіні реалізуються через властивості CALayer: shadowColor задає колір тіні, shadowOffset — зміщення в CGSize (x, y), shadowRadius — радіус розмиття, shadowOpacity — непрозорість від 0 до 1. Для продуктивності також рекомендується задавати shadowPath — UIBezierPath, який описує форму тіні, щоб Core Animation не обчислювала її автоматично.

swift
// UIKit: налаштування тіні через CALayer
import UIKit

let cardView = UIView()
cardView.backgroundColor = .systemBackground
cardView.layer.cornerRadius = 12

// Налаштування тіні
cardView.layer.shadowColor = UIColor.black.cgColor
cardView.layer.shadowOpacity = 0.15
cardView.layer.shadowOffset = CGSize(width: 0, height: 4)
cardView.layer.shadowRadius = 8

"> Optimзация: явный shadowPath
cardView.layer.shadowPath = UIBezierPath(
    roundedRect: cardView.bounds,
    cornerRadius: 12
).cgPath

У SwiftUI тіні задаються через модифікатор .shadow() з параметрами color, radius, x, y. SwiftUI автоматично застосовує shadowPath та керує продуктивністю рендерингу. Для розширених тіней (внутрішні тіні, множинні тіні) використовуйте overlay() з LinearGradient.

swift
// SwiftUI: тінь через модифікатор shadow
import SwiftUI

VStack {
    Text("Картка з тінню")
        .padding()
        .background(Color.white)
        .cornerRadius(16)
        .shadow(
            color: .black.opacity(0.15),
            radius: 10,
            x: 0,
            y: 4
        )
}

Продуктивність тіней на iOS: якщо shadowPath не задано, Core Animation обчислює тінь на основі форми шару кожен кадр, що може викликати затримки під час анімації. Для статичних елементів завжди задавайте shadowPath. Для анімованих елементів використовуйте shouldRasterize = true на CALayer, щоб кешувати тінь в окремий шар. У SwiftUI растерізація керується через .drawingGroup().

Тіні в Android: elevation та кастомні тіні

В Android тіні в Material Design реалізуються через elevation — властивість View, яка задає висоту елемента в z-координаті. Система автоматично відтворює тінь на основі elevation та outlineProvider (контуру елемента). Чим вища elevation, тим більша та розмитіша тінь. Для View, які не успадковують Material Design (наприклад, AppCompat), тіні працюють тільки при увімкненому апаратному прискоренні.

xml
<!-- XML: elevation через атрибути -->
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardCornerRadius="12dp"
    app:cardElevation="6dp">
    <!-- Вміст картки -->
</androidx.cardview.widget.CardView>

Для кастомних тіней в Android View System використовується OutlineProvider. ViewOutlineProvider дозволяє задати довільну форму через outline.setRoundRect() або outline.setPath(). Без OutlineProvider тінь відтворюється за прямокутною формою View, що не враховує округлення кутів.

kotlin
// Kotlin: кастомний OutlineProvider для тіні
import android.graphics.Outline
import android.view.View
import android.view.ViewOutlineProvider

val myView: View = findViewById(R.id.myView)
myView.outlineProvider = object : ViewOutlineProvider() {
    override fun getOutline(view: View, outline: Outline) {
        outline.setRoundRect(
            0, 0, view.width, view.height,
            12f  // радіус кута
        )
    }
}
myView.elevation = 8f  // висота тіні в px
myView.translationZ = 4f  // додаткова висота при натисканні

У Jetpack Compose тіні керуються через Modifier.shadow() та elevation у компонентах Card та Surface. Modifier.shadow() приймає elevation та shape. На відміну від View System, Compose відтворює тіні у власному рендер-пайплайні, що дає більш передбачувану поведінку на всіх версіях Android.

kotlin
// Jetpack Compose: тінь через Modifier.shadow
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

Card(
    modifier = Modifier.shadow(
        elevation = 6.dp,
        shape = RoundedCornerShape(12.dp),
        clip = false
    ),
    colors = CardDefaults.cardColors(containerColor = Color.White)
) {
    Text("Card з кастомною тінню")
}

Рекомендації щодо використання тіней

Правильне використання тіней покращує сприйняття інтерфейсу, неправильне — створює візуальний шум та погіршує продуктивність. Material Design визначає п'ять рівнів elevation: 0 dp (без тіні), 1–4 dp (кнопки, чипси), 6–12 dp (картки, меню), 16–24 dp (діалоги, боттом-шити), 24+ dp (модальні вікна, FAB).

РівеньElevationЕлементиХарактеристика тіні
00 dpТекст, іконки, фонБез тіні, плоский шар
11–4 dpКнопки, поля введенняЛегка тінь, невелике зміщення
26–12 dpКартки, SnackbarПомірна тінь, помітне розмиття
316–24 dpДіалоги, менюГлибока тінь, велике розмиття
424+ dpМодальні вікнаМаксимальна тінь, елемент парить

Правила хорошого тону: (1) не використовуйте тіні для елементів, які не повинні привертати увагу (фони, роздільники), (2) висота тіні повинна відповідати важливості елемента — чим важливіше, тим вище, (3) при натисканні на кнопку зменшуйте elevation анімовано для тактильного зворотного зв'язку, (4) уникайте тіней на темних фонах — використовуйте overlay (освітлення/затемнення) замість тіні. У Material You (Android 12+) тіні адаптуються під тему: на темному фоні elevation замінюється кольоровим шаром (surface tint).

Тіні у Flutter: BoxShadow та PhysicalModel

У Flutter тіні реалізуються через BoxShadow всередині BoxDecoration. BoxShadow приймає color, offset, blurRadius та spreadRadius. Flutter підтримує множинні тіні: передача списку BoxShadow дозволяє створювати складні ефекти — наприклад, зовнішню тінь + внутрішнє підсвічування. PhysicalModel — альтернатива, яка симулює фізичну тінь на основі форми (BeveledRectangleBorder, CircleBorder).

dart
// Flutter: тінь через BoxDecoration + BoxShadow
import 'package:flutter/material.dart';

Container(
    decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.0),
        boxShadow: [
            BoxShadow(
                color: Colors.black.withOpacity(0.15),
                offset: Offset(0, 4),
                blurRadius: 8.0,
                spreadRadius: 0,
            ),
        ],
    ),
    child: Text('Контейнер з тінню'),
)

// PhysicalModel для фізичної тіні
PhysicalModel(
    color: Colors.white,
    elevation: 6.0,
    shadowColor: Colors.black.withOpacity(0.2),
    borderRadius: BorderRadius.circular(12.0),
    child: Text('Фізична модель з тінню'),
)

Продуктивність: BoxShadow з множинними тінями може викликати перемальовування при кожній зміні. Для статичних елементів використовуйте PhysicalModel — він кешує тінь в окремий шар (RepaintBoundary). У Flutter 3.16+ додана підтримка Material 3 elevation з адаптивними тінями, які змінюють колір та розмиття залежно від теми (світла/темна).

Часто задавані питання

Чим elevation відрізняється від shadow?

Elevation — це числова висота елемента в z-координаті, а shadow (тінь) — візуальний ефект, створюваний цією висотою. В Android elevation — властивість View, яка автоматично генерує тінь. У дизайні elevation та shadow часто використовуються як взаємозамінні терміни, але технічно elevation — причина, shadow — наслідок.

Чому тінь не відображається на Android?

Перевірте: (1) у View задано elevation більше 0, (2) задано outlineProvider (якщо View не з бібліотеки Material), (3) включено апаратне прискорення (android:hardwareAccelerated="true" у маніфесті). CardView автоматично керує outlineProvider. Для кастомних View викличте view.outlineProvider = ViewOutlineProvider.BACKGROUND.

Як зробити внутрішню тінь (inner shadow) на iOS?

CALayer не підтримує внутрішні тіні безпосередньо. Використовуйте комбінацію: CAGradientLayer з чорним кольором та прозорістю всередині маски (mask), або накладіть UIImageView з растровою внутрішньою тінню. У SwiftUI внутрішня тінь реалізується через overlay з LinearGradient та blendMode(.multiply).

Як анімувати тінь при натисканні на кнопку?

В Android використовуйте translationZ для анімації: в стані спокою elevation = 2 dp, при натисканні translationZ = 4 dp (сумарна висота 6 dp). На iOS анімуйте shadowOffset та shadowRadius одночасно з трансформацією кнопки. У SwiftUI використовуйте withAnimation зі зміною elevation через Modifier.shadow(). У Flutter — AnimatedContainer зі зміною BoxShadow.

Скільки тіней можна використовувати на одному екрані?

Material Design рекомендує не більше трьох рівнів elevation на одному екрані. Якщо елементів з тінню більше — об'єднуйте їх у групи з однаковою висотою. Надмірна кількість тіней створює візуальний шум та знижує продуктивність рендерингу на пристроях середнього та бюджетного сегмента.

Підсумки

  • Тінь (shadow) — візуальний ефект глибини, заснований на elevation елемента в z-координаті.
  • На iOS тіні налаштовуються через CALayer: shadowColor, shadowOffset, shadowRadius, shadowOpacity.
  • В Android тіні керуються через elevation та OutlineProvider для кастомної форми.
  • Material Design визначає п'ять рівнів elevation: 0, 1–4, 6–12, 16–24 та 24+ dp.
  • Jetpack Compose використовує Modifier.shadow() та elevation у Card/Surface для керування тінями.
  • Flutter реалізує тіні через BoxShadow (BoxDecoration) та PhysicalModel (фізична тінь).
  • Оптимізація: задавайте shadowPath (iOS), outlineProvider (Android), використовуйте PhysicalModel (Flutter).

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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