Published on

PhaseAnimator va KeyframeAnimator

Authors

iOS 17 ikki yangi kuchli animatsiya API qo'shdi: PhaseAnimator — bosqichli, avtomatik aylanadigan animatsiyalar va KeyframeAnimator — professional darajadagi kalit kadr animatsiyalar.

PhaseAnimator — bosqichli animatsiya

// ═══════════════════════════════════════
//  PHASEANIMATOR — cheksiz aylanuvchi
// ═══════════════════════════════════════
// Bosqichlar enum
enum PulseBosqichi: CaseIterable {
    case kichik, katta
}

struct PulseKorinishi: View {
    var body: some View {
        // Bosqichlar orqali avtomatik aylanadi
        PhaseAnimator(PulseBosqichi.allCases) { bosqich in
            Image(systemName: "heart.fill")
                .font(.system(size: 60))
                .foregroundStyle(.red)
                .scaleEffect(bosqich == .katta ? 1.3 : 1.0)
                .opacity(bosqich == .katta ? 1.0 : 0.6)
        } animation: { bosqich in
            // Har bosqich uchun animatsiya turi
            switch bosqich {
            case .kichik: .easeIn(duration: 0.3)
            case .katta: .easeOut(duration: 0.6)
            }
        }
    }
}


// ═══════════════════════════════════════
//  KO'P BOSQICHLI ANIMATSIYA
// ═══════════════════════════════════════
enum YuklashBosqichi: CaseIterable {
    case boshlangich, yuklanmoqda, tugallandi, qaytish
}

struct YuklashAnimatsiyasi: View {
    var body: some View {
        PhaseAnimator(YuklashBosqichi.allCases) { bosqich in
            Circle()
                .fill(rangOlish(bosqich))
                .frame(width: hajmOlish(bosqich))
                .rotationEffect(.degrees(burchakOlish(bosqich)))
        } animation: { _ in
            .spring(duration: 0.8, bounce: 0.3)
        }
    }

    func rangOlish(_ b: YuklashBosqichi) -> Color {
        switch b {
        case .boshlangich: .blue
        case .yuklanmoqda: .purple
        case .tugallandi: .green
        case .qaytish: .blue
        }
    }

    func hajmOlish(_ b: YuklashBosqichi) -> CGFloat {
        switch b {
        case .boshlangich: 50
        case .yuklanmoqda: 80
        case .tugallandi: 60
        case .qaytish: 50
        }
    }

    func burchakOlish(_ b: YuklashBosqichi) -> Double {
        switch b {
        case .boshlangich: 0
        case .yuklanmoqda: 180
        case .tugallandi: 360
        case .qaytish: 0
        }
    }
}

PhaseAnimator — trigger bilan

// ═══════════════════════════════════════
//  TRIGGER — tugma bosilganda animatsiya
// ═══════════════════════════════════════
struct TriggerMisol: View {
    @State private var bosishSoni = 0

    var body: some View {
        VStack(spacing: 30) {
            // trigger o'zgarganda animatsiya qayta ishlaydi
            PhaseAnimator(
                [false, true],
                trigger: bosishSoni
            ) { bosqich in
                Image(systemName: "star.fill")
                    .font(.system(size: 50))
                    .foregroundStyle(.yellow)
                    .scaleEffect(bosqich ? 1.5 : 1.0)
                    .rotationEffect(.degrees(bosqich ? 360 : 0))
            } animation: { bosqich in
                bosqich ? .spring(bounce: 0.5) : .easeIn(duration: 0.1)
            }

            Button("Animatsiya") {
                bosishSoni += 1  // trigger o'zgaradi
            }
        }
    }
}

KeyframeAnimator — kalit kadr animatsiya

// ═══════════════════════════════════════
//  KEYFRAMEANIMATOR — murakkab animatsiya
// ═══════════════════════════════════════

// Animatsiya qiymatlari uchun struct
struct RaketaAnimatsiya {
    var масштаб = 1.0       // scale
    var surish: CGFloat = 0  // vertical offset
    var burchak = 0.0        // rotation
    var shaffoflik = 1.0     // opacity
}

struct RaketaKorinishi: View {
    @State private var uchirish = false

    var body: some View {
        VStack {
            KeyframeAnimator(
                initialValue: RaketaAnimatsiya(),
                trigger: uchirish
            ) { qiymat in
                Image(systemName: "airplane")
                    .font(.system(size: 60))
                    .scaleEffect(qiymat.масштаб)
                    .offset(y: qiymat.surish)
                    .rotationEffect(.degrees(qiymat.burchak))
                    .opacity(qiymat.shaffoflik)
            } keyframes: { _ in
                // Scale animatsiyasi
                KeyframeTrack(\.масштаб) {
                    SpringKeyframe(1.2, duration: 0.3)
                    CubicKeyframe(0.8, duration: 0.2)
                    SpringKeyframe(1.5, duration: 0.5)
                }
                // Vertikal harakat
                KeyframeTrack(\.surish) {
                    LinearKeyframe(0, duration: 0.3)
                    SpringKeyframe(-30, duration: 0.3)
                    CubicKeyframe(-200, duration: 0.5)
                }
                // Aylanish
                KeyframeTrack(\.burchak) {
                    LinearKeyframe(0, duration: 0.3)
                    CubicKeyframe(-30, duration: 0.3)
                    LinearKeyframe(-45, duration: 0.5)
                }
                // Shaffoflik
                KeyframeTrack(\.shaffoflik) {
                    LinearKeyframe(1.0, duration: 0.6)
                    CubicKeyframe(0.0, duration: 0.4)
                }
            }

            Button("Uchirish! 🚀") {
                uchirish.toggle()
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

🎯 Topshiriq

PhaseAnimator bilan "loading spinner" yarating — 4 bosqich, har bosqichda rang, hajm va burchak o'zgarsin. KeyframeAnimator bilan "bouncing ball" yarating — vertikal harakat (sakrash), gorizontal (oldinga) va siqilish effekti.

Buy mea coffee