Post not yet marked as solved
Hi,
When defining a KeyFrameAnimation with spring tracks, the animation do not complete until the very end , but, as default when using withAnimation with a spring animation, it will start the next animation
For example if a complete spring animation is 600ms, the next animation may start only after 300ms
My designer has given me the mass, stiffness, and dumping values for the animation, and expects that the animation will perform for X time, and after that a new animation will start, as it occurs on figma
Using withAnimation with completition callback i can replicate the behaviour, using the ".removed" CompletitionCriteria, but i'd rather use the new KeyFrame phase animator, because i'm not very confident having 5-6 animations started recursively with "withAnimation"
Post not yet marked as solved
I watched both sessions. At the end of the keyframe animations session, the presenter at 16:42 shows the KeyFrameTimeline and its API.
// Keyframes
let myKeyframes = KeyframeTimeline(initialValue: CGPoint.zero) {
KeyframeTrack(\.x) {...}
KeyframeTrack(\.y) {...}
}
// Duration in seconds
let duration: TimeInterval = myKeyframes.duration
// Value for time
let value = myKeyframes.value (time: 1.2)
Then he says that he used this to draw the charts seen earlier in the WWDC sessions.
I thought that he used the instance of a KeyFrameTimeline directly in Chart() to make a graph of the variables and values over time. If trying to to it I get the error message that KeyFrameTimeline needs to conform to Random Access Collection.
So maybe I am missing something? I am curious to see how that would work! And how to make a graph for each KeyframeTrack inside my KeyframeTimeline
Post not yet marked as solved
Hi
I'm trying to use the new PhaseAnimator from iOS 17 and I noticed a bug.
For some reason, the animation is not deallocated when the view is removed from hierarchy, leading to a crash app.
Example
enum OuterBreathState: CaseIterable {
case exhale, inhale
var scale: CGFloat {
switch self {
case .inhale: return 2
case .exhale: return 1
}
}
var color: Color {
switch self {
case .inhale: return .white
case .exhale: return .blue
}
}
}
struct PhaseView: View {
var body: some View {
PhaseAnimator(OuterBreathState.allCases) { state in
Circle()
.background(.clear)
.frame(width: 150, height: 150)
.padding(.vertical)
.scaleEffect(state.scale)
.background(in: Circle())
.foregroundStyle(state.color)
} animation: {state in
switch state {
case .inhale: return .easeInOut(duration: 1.5)
case .exhale: return .easeInOut(duration: 3.5)
}
}
}
}
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink("push") {
PhaseView()
}
}
}
}
Pushing the view works just fine, but after popping, the app becomes unresponsive 😓
It's the content update that fails actually. If I make no updates to the Circle, everything works fine.
I also tried with the phaseAnimator trailing function, I get the same behaviour.
Interesting fact : if I use a trigger (and even a timer that flips that trigger to mimic the infinite loop), it does not crash the app.
Here is the code
enum OuterBreathState: CaseIterable {
case exhale, inhale
var scale: CGFloat {
switch self {
case .inhale: return 2
case .exhale: return 1
}
}
var color: Color {
switch self {
case .inhale: return .white
case .exhale: return .blue
}
}
}
struct PhaseView: View {
@State var animate = false
let timer = Timer.publish(every: 5, tolerance: .zero, on: .main, in: .default).autoconnect()
var body: some View {
PhaseAnimator(OuterBreathState.allCases, trigger: animate) { state in
Circle()
.background(.clear)
.frame(width: 150, height: 150)
.padding(.vertical)
.scaleEffect(state.scale)
.background(in: Circle())
.foregroundStyle(state.color)
} animation: {state in
switch state {
case .inhale: return .easeInOut(duration: 1.5)
case .exhale: return .easeInOut(duration: 3.5)
}
}
.onReceive(timer, perform: { _ in
animate.toggle()
})
.onTapGesture {
animate.toggle()
}
}
}
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink("push") {
PhaseView()
}
}
}
}
#Preview {
ContentView()
}
Any idea why it crashes and how I can make it work ?
Thanks!