Как оживить путь в SwiftUI - PullRequest
1 голос
/ 04 июля 2019

Будучи незнакомым с 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)
    }
}

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

enter image description here

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

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

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

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

1 Ответ

6 голосов
/ 04 июля 2019

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

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


Обновление:

Я работал с анимацией контуров формы.Вот GIF.

enter image description here

А вот код:

ВАЖНО: Код не анимируется наПредварительный просмотр 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 }
    }
}
...