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 rasterization управляется через .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  // corner radius
        )
    }
}
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 с множественными тенями может вызывать перерисовку при каждом изменении (repaint). Для статичных элементов используйте PhysicalModel — он кэширует тень в отдельный слой (RepaintBoundary). Во Flutter 3.16+ добавлена поддержка Material 3 elevation с адаптивными тенями, которые меняют цвет и размытие в зависимости от темы (light/dark).

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

Чем 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 года. Мы проконсультируем вас и предложим наилучшее решение.

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

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