Shadow (сенка) — визуален ефект в мобилните интерфейси, който създава илюзия за дълбочина и йерархия между елементите. Сенките показват кой елемент се намира по-високо в Z-равнината и следователно е по-важен или интерактивен. Според Material Design Guidelines (Google, 2026), системните сенки използват пет ключови височини (elevation): 0 dp (плосък), 1–4 dp (бутони), 6–12 dp (карти), 16–24 dp (диалози), 24+ dp (модални прозорци). Повече за височината на елементите прочетете в статията за Elevation.
Основни точки
Сенка (shadow) в потребителските интерфейси — визуален ефект, имитиращ осветление и дълбочина. Сенката се създава чрез рисуване на тъмно размито копие на елемента, изместено спрямо оригинала. В мобилния дизайн сенките изпълняват три функции: показват йерархия (по-висок елемент — по-дълга сенка), сигнализират за интерактивност (бутоните със сенка изглеждат кликаеми), отделят съдържанието от фона (картите със сенка се четат като отделни блокове).
В Material Design на Google сенките са неразривно свързани с концепцията за elevation (височина). Всеки елемент заема определена височина в Z-координатата, а сенката е проекция на осветление отгоре. Human Interface Guidelines на Apple не предписват строга система от височини, но препоръчват използването на сенки за създаване на визуална йерархия и дълбочина в интерфейсите.
Според изследване на Nielsen Norman Group (2024), правилно поставените сенки намаляват времето за намиране на интерактивен елемент с 24%. Сенките също помагат на потребители със зрителни увреждания да различават границите на елементите. Прекаленото използване на сенки (3+ нива на екран) обаче увеличава когнитивното натоварване и влошава възприятието.
На iOS сенките се реализират чрез свойствата на CALayer: shadowColor задава цвета на сенката, shadowOffset — отместването в CGSize (x, y), shadowRadius — радиуса на размиване, shadowOpacity — непрозрачността от 0 до 1. За производителност се препоръчва също задаването на shadowPath — UIBezierPath, който описва формата на сенката, за да не я изчислява Core Animation автоматично.
// 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.
// 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 сенките в Material Design се реализират чрез elevation — свойството на View, което задава височината на елемента в Z-координатата. Системата автоматично рисува сенка въз основа на elevation и outlineProvider (контура на елемента). Колкото по-високо е elevation, толкова по-голяма и размита е сенката. За View, които не наследяват Material Design (напр. AppCompat), сенките работят само с включено хардуерно ускорение.
<!-- 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: персонализиран 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.
// 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 | Елементи | Характеристика на сенката |
|---|---|---|---|
| 0 | 0 dp | Текст, икони, фон | Без сенка, плосък слой |
| 1 | 1–4 dp | Бутони, полета за въвеждане | Лека сенка, малко отместване |
| 2 | 6–12 dp | Карти, Snackbar | Умерена сенка, забележимо размиване |
| 3 | 16–24 dp | Диалози, менюта | Дълбока сенка, голямо размиване |
| 4 | 24+ dp | Модални прозорци | Максимална сенка, елементът плува |
Правила за добра практика: (1) не използвайте сенки за елементи, които не трябва да привличат внимание (фон, разделители), (2) височината на сенката трябва да съответства на важността на елемента — колкото по-важен, толкова по-високо, (3) при натискане на бутон намалявайте elevation анимирано за тактилна обратна връзка, (4) избягвайте сенки на тъмни фонове — използвайте overlay (изсветляване/потъмняване) вместо сенка. В Material You (Android 12+) сенките се адаптират към темата: на тъмен фон elevation се заменя с цветен слой (surface tint).
Във Flutter сенките се реализират чрез BoxShadow вътре в BoxDecoration. BoxShadow приема color, offset, blurRadius и spreadRadius. Flutter поддържа множество сенки: предаването на списък от BoxShadow позволява създаването на сложни ефекти — например външна сенка + вътрешно осветление. PhysicalModel — алтернатива, която симулира физическа сенка въз основа на форма (BeveledRectangleBorder, CircleBorder).
// 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 с адаптивни сенки, които променят цвета и размиването в зависимост от темата (светла/тъмна).
Често задавани въпроси
Elevation — числовата височина на елемента в Z-координатата, а shadow (сенка) — визуалният ефект, създаден от тази височина. В Android elevation — свойство на View, което автоматично генерира сенка. В дизайна elevation и shadow често се използват като взаимозаменяеми термини, но технически elevation е причината, а shadow — следствието.
Проверете: (1) дали View има elevation по-голямо от 0, (2) дали е зададен outlineProvider (ако View не е от Material библиотека), (3) дали е включено хардуерно ускорение (android:hardwareAccelerated="true" в манифеста). CardView автоматично управлява outlineProvider. За персонализирани View извикайте view.outlineProvider = ViewOutlineProvider.BACKGROUND.
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 на един екран. Ако има повече елементи със сенка — обединете ги в групи с еднаква височина. Прекаленият брой сенки създава визуален шум и намалява производителността на рендиране на устройства от среден и бюджетен сегмент.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също