ios-native

طراحی انیمیشن‌های بومی و سریع iOS با SwiftUI، UIKit و Core Animation

این مهارت برای ساخت انیمیشن‌های native در اپلیکیشن‌های iOS، iPadOS و macOS با استفاده از SwiftUI، UIKit و Core Animation است. از transitionهای ساده تا انیمیشن‌های لایه‌ای، spring و کنترل فریم‌به‌فریم را پوشش می‌دهد.

با تکیه بر الگوهای حرکتی Material Design 3، زمان‌بندی استاندارد، gestureهای قابل‌قطع و پشتیبانی از Reduce Motion، تجربه‌ای روان، قابل‌دسترس و هماهنگ با پلتفرم Apple بسازید.

چه چیزی می‌گیرید

  • انتخاب API مناسب بین SwiftUI، UIView.animate، UIViewPropertyAnimator، Core Animation و CADisplayLink
  • پیاده‌سازی transition، matched geometry، spring، keyframe و path animation
  • زمان‌بندی و easing هماهنگ با الگوهای Material Design 3
  • پشتیبانی از gestureهای تعاملی، انیمیشن‌های قابل‌برگشت و haptic feedback
  • رعایت accessibility با تشخیص UIAccessibility.isReduceMotionEnabled

برای چه کاری خوب است

  • اپلیکیشن‌های iOS و iPadOS ساخته‌شده با SwiftUI یا UIKit
  • طراحی transitionهای صفحه، micro-interactionها و انیمیشن‌های gesture-driven
  • پروژه‌هایی که performance native و هماهنگی با الگوهای Apple اهمیت دارد

برای چه کاری نیست

  • انیمیشن‌های وب با JavaScript، CSS یا کتابخانه‌هایی مانند GSAP
  • ساخت ویدیو، motion graphics یا خروجی مستقل از اپلیکیشن‌های Apple
  • پروژه‌هایی که به کدنویسی Swift یا دسترسی به Apple SDKها نیاز ندارند

دربارهٔ این اسکیل

iOS Native Animation

Native iOS animation APIs for maximum performance and platform consistency.

API Selection

APIUse CasePerformance
SwiftUIDeclarative UI, modern appsExcellent
UIView.animateSimple UIKit animationsGood
UIViewPropertyAnimatorInterruptible, gesture-drivenExcellent
Core AnimationComplex, layer-basedBest
CADisplayLinkFrame-by-frame controlManual

SwiftUI Animations

Basic Transitions

// Implicit animation
@State private var isExpanded = false

Rectangle()
    .frame(width: isExpanded ? 200 : 100)
    .animation(.easeInOut(duration: 0.3), value: isExpanded)

// Explicit animation
withAnimation(.spring(response: 0.5, dampingFraction: 0.7)) {
    isExpanded.toggle()
}

M3-Aligned Springs

extension Animation {
    // M3 fast spatial (small elements)
    static let m3FastSpatial = Animation.spring(
        response: 0.3,
        dampingFraction: 0.9
    )

    // M3 default spatial (standard UI)
    static let m3DefaultSpatial = Animation.spring(
        response: 0.5,
        dampingFraction: 0.9
    )

    // M3 slow spatial (large elements)
    static let m3SlowSpatial = Animation.spring(
        response: 0.7,
        dampingFraction: 0.9
    )

    // M3 effects (opacity, color - no overshoot)
    static let m3Effects = Animation.spring(
        response: 0.35,
        dampingFraction: 1.0
    )
}

Transitions

// Container transition equivalent
.transition(.asymmetric(
    insertion: .scale(scale: 0.95).combined(with: .opacity),
    removal: .scale(scale: 0.95).combined(with: .opacity)
))

// Matched geometry for shared element
@Namespace private var animation

// Source
Image("photo")
    .matchedGeometryEffect(id: "hero", in: animation)

// Destination
Image("photo")
    .matchedGeometryEffect(id: "hero", in: animation)

UIKit Animations

UIView.animate

// M3 standard timing
UIView.animate(
    withDuration: 0.3,
    delay: 0,
    options: [.curveEaseOut],
    animations: {
        view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
        view.alpha = 1.0
    }
)

// M3 emphasized-decelerate (entrance)
UIView.animate(
    withDuration: 0.25,
    delay: 0,
    usingSpringWithDamping: 0.9,
    initialSpringVelocity: 0.5,
    options: [],
    animations: {
        view.transform = .identity
    }
)

UIViewPropertyAnimator (Interruptible)

// Create interruptible animation
let animator = UIViewPropertyAnimator(
    duration: 0.3,
    dampingRatio: 0.9
) {
    view.transform = .identity
}

// Start
animator.startAnimation()

// Pause mid-animation
animator.pauseAnimation()

// Reverse
animator.isReversed = true
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)

// Scrub with gesture
animator.fractionComplete = panGesture.translation.y / maxDistance

Core Animation

CABasicAnimation

// Position animation
let positionAnim = CABasicAnimation(keyPath: "position.y")
positionAnim.fromValue = 0
positionAnim.toValue = 100
positionAnim.duration = 0.3
positionAnim.timingFunction = CAMediaTimingFunction(
    controlPoints: 0.2, 0, 0, 1  // M3 standard
)
layer.add(positionAnim, forKey: "position")

// Don't forget to set final value
layer.position.y = 100

CAKeyframeAnimation

// Shake animation
let shake = CAKeyframeAnimation(keyPath: "transform.translation.x")
shake.values = [0, -10, 10, -8, 8, -5, 5, 0]
shake.keyTimes = [0, 0.1, 0.25, 0.4, 0.55, 0.7, 0.85, 1]
shake.duration = 0.5
layer.add(shake, forKey: "shake")

// Path animation
let pathAnim = CAKeyframeAnimation(keyPath: "position")
pathAnim.path = arcPath.cgPath
pathAnim.duration = 0.5
pathAnim.rotationMode = .rotateAuto
layer.add(pathAnim, forKey: "follow")

CASpringAnimation

let spring = CASpringAnimation(keyPath: "transform.scale")
spring.fromValue = 0.8
spring.toValue = 1.0
spring.mass = 1.0
spring.stiffness = 300  // M3 slow
spring.damping = 20     // Approximates 0.9 ratio
spring.initialVelocity = 0
spring.duration = spring.settlingDuration
layer.add(spring, forKey: "spring")

M3 Timing Functions

extension CAMediaTimingFunction {
    // M3 standard
    static let m3Standard = CAMediaTimingFunction(
        controlPoints: 0.2, 0, 0, 1
    )

    // M3 emphasized-decelerate (entrance)
    static let m3EmphasizedDecelerate = CAMediaTimingFunction(
        controlPoints: 0.05, 0.7, 0.1, 1
    )

    // M3 emphasized-accelerate (exit)
    static let m3EmphasizedAccelerate = CAMediaTimingFunction(
        controlPoints: 0.3, 0, 0.8, 0.15
    )

    // M3 standard-decelerate
    static let m3StandardDecelerate = CAMediaTimingFunction(
        controlPoints: 0, 0, 0, 1
    )
}

Duration Scale

TokeniOS DurationUse
short-10.05sMicro-interactions
short-40.2sButtons, controls
medium-20.3sDialogs, sheets
long-20.5sPage transitions

Reduced Motion

// Check user preference
if UIAccessibility.isReduceMotionEnabled {
    // Use instant or fade-only
    UIView.animate(withDuration: 0.1) {
        view.alpha = 1.0
    }
} else {
    // Full animation
    UIView.animate(
        withDuration: 0.3,
        usingSpringWithDamping: 0.9,
        initialSpringVelocity: 0
    ) {
        view.transform = .identity
        view.alpha = 1.0
    }
}

// SwiftUI
@Environment(\.accessibilityReduceMotion) var reduceMotion

.animation(reduceMotion ? .none : .spring(), value: isExpanded)

Haptic Feedback

// Pair animations with haptics
let impact = UIImpactFeedbackGenerator(style: .medium)
impact.prepare()

UIView.animate(...) {
    // Animation
} completion: { _ in
    impact.impactOccurred()
}

// Success haptic
let notification = UINotificationFeedbackGenerator()
notification.notificationOccurred(.success)

Related Skills

  • Dependencies: easing, duration, spring
  • See also: platform-adaptation, accessibility
  • Tokens: tokens/ios-motion.tokens.json

دانلود ZIP