I have found that this trim animation here animate path stroke drawing in SwiftUI works as expected if the MyLines view appears in the ContentView (root view) of the app or when it appears as destination of a navigation link. However, SwiftUI seems to ignore the animation duration if the view appears inside of a custom view - I created an overlay view that transitions in from the right. I only took that line trimming animation as an example - this bug (if it is one) also seems to occur with other animations, e.g. some view changing its height on appear.
I tried changing the duration. if I double it (e.g. from 2 seconds to 4), the actual animation duration does not seem to change...
struct ContentView: View {
@State var showOverlay: Bool = false
var body: some View {
NavigationView {
VStack {
NavigationLink("My Lines (Nav Link)", destination: MyLines(height: 200, width: 250))
Button(action: {
self.showOverlay.toggle()
}, label: {
Text("My Lines (overlay)")
})
}
}.overlayView(content: {
VStack {
HStack{
Button(action: { self.showOverlay = false}, label: {
Text("Back")
})
Spacer()
}.padding(.top, 40).padding(.horizontal, 15)
MyLines(height: 200, width: 250)
Spacer()
}
}, background: {
Color(.systemBackground)
}, show: $showOverlay, size: nil, transition: AnyTransition.move(edge: .trailing).animation(.easeInOut(duration: 0.3)))
}
}
Here is the code of MyLine again - I deliberately delay the animation by 1 second so that it becomes clear that the problem is not caused by a too long transition of the overlay view in which the Line view exists.
import SwiftUI
struct MyLines: View { var height: CGFloat var width: CGFloat
@State private var percentage: CGFloat = .zero
var body: some View {
Path { path in
path.move(to: CGPoint(x: 0, y: height/2))
path.addLine(to: CGPoint(x: width/2, y: height))
path.addLine(to: CGPoint(x: width, y: 0))
}
.trim(from: 0, to: percentage) // << breaks path by parts, animatable
.stroke(Color.black, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
withAnimation(.easeOut(duration: 2.0)) {
self.percentage = 1.0
}
})
}.padding()
}
}
I have set up a complete project here that should illustrate the problem and also includes the overlay view: https://github.com/DominikButz/TrimAnimationBug
By the way, this duration problem does not disappear after removing NavigationView and only use the overlay view.
You might wonder, why use the custom overlay instead of the boiler plate navigation? I found that NavigationView causes a lot of problems:
- difficult to change the background colour
- does not support custom transitions, e.g. with a matched geometry effect (the overlay view I created can appear with any type of transition)
- causes weird crashes (e.g. related to matchedGeometryEffect)
- prevents the .hideStatusBar modifier to work properly etc. etc.