Как оживить путь в SwiftUI

Будучи незнакомым со SwiftUI и тем фактом, что пока еще не так много документации по этой новой среде. Мне было интересно, если кто-нибудь был знаком с тем, как можно было бы оживить Path в SwiftUI.

Например, учитывая вид, скажем так RingView:

struct RingView : View {   
    var body: some View {
        GeometryReader { geometry in
            Group {
                // create outer ring path
                Path { path in
                    path.addArc(center: center,
                                radius: outerRadius,
                                startAngle: Angle(degrees: 0),
                                endAngle: Angle(degrees: 360),
                                clockwise: true)
                }
                .stroke(Color.blue)

                // create inner ring
                Path { path in
                    path.addArc(center: center,
                                radius: outerRadius,
                                startAngle: Angle(degrees: 0),
                                endAngle: Angle(degrees: 180),
                                clockwise: true)
                }
                .stroke(Color.red)
                .animation(.basic(duration: 2, curve: .linear))
            }
        }
        .aspectRatio(1, contentMode: .fit)
    }
}

Что отображается:

Теперь мне было интересно, как мы можем анимировать внутреннее кольцо, то есть красную линию внутри синей линии. Анимация, которую я ищу, была бы простой анимацией, где путь появляется от начала и проходит до конца.

Это довольно просто, используя CoreGraphics и старый фреймворк UIKit, но это не похоже на добавление простого .animation(.basic(duration: 2, curve: .linear)) на внутренний путь и отображение вида с withAnimation Блок делает что угодно.

Я просмотрел предоставленные Apple руководства по SwiftUI, но они действительно охватывают анимацию перемещения / масштабирования только в более подробных представлениях, таких как Image,

Любое руководство о том, как оживить Path или же Shape в SwiftUI?

1 ответ

Решение

Анимация путей демонстрируется в сеансе WWDC 237 ( Создание пользовательских видов с помощью SwiftUI). Ключ использует AnimatableData. Вы можете подскочить до 31:23, но я рекомендую начать хотя бы с минуты 27:47.

Вам также нужно будет загрузить пример кода, потому что удобно, интересные фрагменты не показаны (и не объяснены) в презентации: https://developer.apple.com/documentation/swiftui/drawing_and_animation/building_custom_views_in_swiftui


Дополнительная документация: Поскольку я первоначально опубликовал ответ, я продолжил исследовать, как анимировать Path, и опубликовал статью с подробным объяснением протокола Animatable и его использования с Paths: https://swiftui-lab.com/swiftui-animations-part1/


Обновить:

Я работал с анимацией пути формы. Вот ГИФ.

И вот код:

ВАЖНО: код не анимируется в предварительных просмотрах Xcode Live. Он должен работать на симуляторе или на реальном устройстве.

import SwiftUI

struct ContentView : View {
    var body: some View {
        RingSpinner().padding(20)
    }
}

struct RingSpinner : View {
    @State var pct: Double = 0.0

    var animation: Animation {
        Animation.basic(duration: 1.5).repeatForever(autoreverses: false)
    }

    var body: some View {

        GeometryReader { geometry in
            ZStack {
                Path { path in

                    path.addArc(center: CGPoint(x: geometry.size.width/2, y: geometry.size.width/2),
                                radius: geometry.size.width/2,
                                startAngle: Angle(degrees: 0),
                                endAngle: Angle(degrees: 360),
                                clockwise: true)
                }
                .stroke(Color.green, lineWidth: 40)

                InnerRing(pct: self.pct).stroke(Color.yellow, lineWidth: 20)
            }
        }
        .aspectRatio(1, contentMode: .fit)
            .padding(20)
            .onAppear() {
                withAnimation(self.animation) {
                    self.pct = 1.0
                }
        }
    }

}

struct InnerRing : Shape {
    var lagAmmount = 0.35
    var pct: Double

    func path(in rect: CGRect) -> Path {

        let end = pct * 360
        var start: Double

        if pct > (1 - lagAmmount) {
            start = 360 * (2 * pct - 1.0)
        } else if pct > lagAmmount {
            start = 360 * (pct - lagAmmount)
        } else {
            start = 0
        }

        var p = Path()

        p.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width/2),
                 radius: rect.size.width/2,
                 startAngle: Angle(degrees: start),
                 endAngle: Angle(degrees: end),
                 clockwise: false)

        return p
    }

    var animatableData: Double {
        get { return pct }
        set { pct = newValue }
    }
}
Другие вопросы по тегам