Реализация Feature Discovery (выделение новых функций) в мобильном приложении
Пользователь обновил приложение. Новая вкладка появилась в Tab Bar. Он её не замечает — потому что смотрит туда, куда привык. Feature discovery решает эту проблему: визуально привлекает внимание к новому элементу в момент, когда пользователь впервые видит обновлённый экран.
Что такое feature discovery технически
Это анимированный визуальный эффект поверх или вокруг целевого UI-элемента: пульсирующий badge, расширяющееся кольцо, мерцающая обводка. Принципиально отличается от coach marks тем, что не перекрывает интерфейс — пользователь может взаимодействовать с остальными элементами. Attention-grabber, а не блокировщик.
Google реализовал это в Material Design через компонент FeatureHighlight в старых версиях Material Components. В текущем Material3 его нет в явном виде, но паттерн остался.
Пульсирующий badge и ring-анимация
iOS. Пульсирующий кружок — CALayer с CABasicAnimation на transform.scale и opacity. Ключевая деталь: расширяющееся кольцо создаётся через два слоя — внутренний остаётся, внешний анимируется от scale 1.0 до 2.5 с opacity от 1.0 до 0.0 в бесконечном цикле (repeatCount = .infinity). CAAnimationGroup синхронизирует обе анимации.
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = 1.0
scaleAnimation.toValue = 2.5
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue = 0.8
opacityAnimation.toValue = 0.0
let group = CAAnimationGroup()
group.animations = [scaleAnimation, opacityAnimation]
group.duration = 1.5
group.repeatCount = .infinity
pulseLayer.add(group, forKey: "pulse")
Слой добавляется поверх target view через targetView.layer.addSublayer(pulseLayer) или в superview.layer если нужен эффект за пределами bounds целевого view.
В SwiftUI — withAnimation(.easeOut(duration: 1.5).repeatForever(autoreverses: false)) на Circle().scale() в overlay. Чище и декларативнее, но repeatForever без autoreverses: false даёт эффект "туда-обратно" — не то что нужно для pulse.
Android Compose. InfiniteTransition + animateFloat:
val infiniteTransition = rememberInfiniteTransition()
val scale by infiniteTransition.animateFloat(
initialValue = 1f, targetValue = 2.5f,
animationSpec = infiniteRepeatable(tween(1500, easing = EaseOut), RepeatMode.Restart)
)
Material Design 3 Feature Highlight
Material3 предлагает FeatureHighlight как часть extended-компонентов (не в основной библиотеке). Для полноценной реализации в стиле Google — библиотека material-components-android-compose-theme-adapter или кастомный компонент.
Стандартный Material3 подход: круглый FloatingActionButton с extended описанием появляется рядом с новой функцией при первом показе экрана, исчезает через 4 секунды или по нажатию. Реализуется через AnimatedVisibility с slideInVertically + fadeIn и LaunchedEffect(Unit) с delay(4000L).
Управление видимостью и персистентность
Feature discovery показывается один раз — при первом появлении экрана после обновления. Логика:
let key = "feature_new_tab_v2_3_0_shown"
if !UserDefaults.standard.bool(forKey: key) {
showFeatureDiscovery(for: newTabButton)
UserDefaults.standard.set(true, forKey: key)
}
Ключ содержит версию (v2_3_0) — при следующем обновлении с новой функцией меняем ключ, пользователи снова увидят discovery. Хранить список показанных discovery в массиве [String] удобнее чем отдельные ключи для каждой фичи.
Задержка показа: 800–1000 мс после viewDidAppear / onAppear. Не показываем мгновенно — пользователь должен сначала увидеть экран в целом.
Комбинирование с другими механиками
Feature discovery часто идёт в паре с badge-счётчиком на иконке таба: красный кружок «NEW» пока пользователь не посетил новый раздел. На iOS — UITabBarItem.badgeValue = "NEW", убираем при tabBarController(_:didSelect:). В Compose — кастомный BadgedBox из Material3.
Если новая функция не в навигации, а внутри экрана — feature discovery комбинируем с Snackbar / Toast с action: «Попробуйте новый фильтр →». По нажатию — скролл к элементу + pulse анимация. UICollectionView.scrollToItem(at:at:animated:) затем с задержкой 300 мс — CALayer pulse.
Срок: 1–3 дня. Одиночный pulse-эффект на конкретном элементе — 1 день. Система с очередью discovery, конфигурацией по версии, несколькими типами анимаций и аналитикой показов — 3 дня.







