Реализация Spring-анимаций (физических) в iOS-приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Реализация Spring-анимаций (физических) в iOS-приложении
Средний
от 1 дня до 3 дней
Часто задаваемые вопросы

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

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Реализация Spring-анимаций (физических) в iOS-приложении

Spring-анимации делают интерфейс живым — элементы не просто перемещаются, они «пружинят», слегка перелетая целевую точку и возвращаясь обратно. iOS использует их везде: иконки при долгом нажатии, карточки в App Store, клавиши клавиатуры. Разница между «сделано как в системе» и «почти как в системе» — в правильных параметрах физической модели.

UIKit: UISpringTimingParameters и UIViewPropertyAnimator

До iOS 10 spring-анимации делали через UIView.animate(withDuration:delay:usingSpringWithDamping:initialSpringVelocity:). Параметры dampingRatio (0–1) и initialVelocity работают, но это упрощённая модель — не настоящая физика пружины.

Начиная с iOS 10 — UISpringTimingParameters с mass, stiffness и damping:

let timingParams = UISpringTimingParameters(
    mass: 1.0,
    stiffness: 170,
    damping: 26,
    initialVelocity: CGVector(dx: 0, dy: 0)
)
let animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParams)
animator.addAnimations {
    self.cardView.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
}
animator.startAnimation()

duration: 0 — при использовании spring timing duration игнорируется, анимация длится столько, сколько нужно пружине для затухания. Это правильно — не задавайте duration для spring.

Параметры для типичных случаев: stiffness: 300, damping: 30 — быстрая упругая анимация (feedback при нажатии). stiffness: 120, damping: 14 — медленная мягкая пружина (появление bottom sheet). stiffness: 400, damping: 40 — жёсткая без перелёта (переключатель).

UIViewPropertyAnimator поддерживает isInterruptible = true — анимацию можно остановить и перенаправить в процессе. Это важно для gesture-driven интерфейсов: если пользователь начал тянуть карточку вниз и передумал — анимация плавно реверсируется с текущей скорости.

SwiftUI: spring() и .interpolatingSpring()

SwiftUI предлагает несколько вариантов:

// Простой spring с dampingFraction
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
    isExpanded.toggle()
}

// Физическая модель через interpolatingSpring
withAnimation(.interpolatingSpring(stiffness: 170, damping: 26)) {
    offset = targetOffset
}

// iOS 17+: новый Spring тип
withAnimation(.spring(.bouncy(duration: 0.4, extraBounce: 0.1))) {
    scale = 1.0
}

.spring(response:dampingFraction:) — проще в использовании: response — это приблизительная длительность (но не жёсткая), dampingFraction 1.0 — критическое затухание без перелёта, меньше 1.0 — перелёт. Для большинства UI-анимаций: response: 0.3–0.5, dampingFraction: 0.7–0.85.

iOS 17 принёс именованные spring presets: .bouncy, .smooth, .snappy — полезны для быстрого прототипирования, но для финального продукта лучше задавать параметры явно.

Velocity matching при прерывании: когда анимация прерывается жестом, новая spring-анимация должна начинаться с текущей скорости. В SwiftUI это через @GestureState и withAnimation с правильным initialVelocity. В UIKit — через UIViewPropertyAnimator.fractionComplete и continueAnimation(withTimingParameters:durationFactor:).

Gesture-driven spring: UIPanGestureRecognizer + spring

Самый живой кейс — карточка, которую можно тянуть, и она spring-ится обратно или долетает до следующей позиции:

@objc func handlePan(_ gesture: UIPanGestureRecognizer) {
    let translation = gesture.translation(in: view)

    switch gesture.state {
    case .changed:
        cardView.transform = CGAffineTransform(translationX: 0, y: translation.y)
    case .ended:
        let velocity = gesture.velocity(in: view)
        let velocityVector = CGVector(dx: 0, dy: velocity.y / 1000) // нормализация

        let timingParams = UISpringTimingParameters(
            mass: 1, stiffness: 200, damping: 28,
            initialVelocity: velocityVector
        )
        let animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParams)
        animator.addAnimations {
            self.cardView.transform = .identity
        }
        animator.startAnimation()
    default: break
    }
}

initialVelocity берём из gesture velocity, нормализуем делением на ~1000 (порядок величин UISpringTimingParameters отличается от points/second жеста).

Сроки

Добавление spring-анимаций к существующим UI-компонентам (2–4 элемента): 1–2 дня с тестированием на устройствах. Gesture-driven интерактивный экран с spring physics — 2–3 дня. Стоимость рассчитывается индивидуально.