UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: How to add dynamic start and end position to animation inside UIViewRepresentable in SwiftUI

Forums > SwiftUI

I have the following UIKit animation inside a UIViewRepresentable which works fine, it animates a view in a throwing effect like animation from point A to point B. What I would like to be able to do is, set the start and the end position in ContentView when assigning the animation to a view.

Here is the code...

Static Position Animation

    import SwiftUI

        struct ContentView: View {
            @State private var isAnimating = false
            var body: some View {
                HStack{
                    Image(systemName: "circle.fill")
                        .font(.system(size: 65))
                        .foregroundColor(.blue)
                        .throwAnimation(isAnimating: $isAnimating)
                        .onTapGesture {
                            isAnimating.toggle()
                        }
                }
            }
        }

        struct ThrowAnimationWrapper<Content: View>: UIViewRepresentable{
            @ViewBuilder let content: () -> Content
            @Binding var isAnimating: Bool
            func makeUIView(context: Context) -> UIView {
                UIHostingController(rootView: content()).view
            }
            func updateUIView(_ uiView: UIView, context: Context) {
                if isAnimating{
                    UIView.animateKeyframes(withDuration: 1.5, delay: 0.0, options: [.calculationModeCubic], animations: {
                        UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: {
                            uiView.center =  CGPoint(x: 250, y: 300) // how can I make this dynamic
                        })
                        UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.9, animations: {
                            uiView.center =  CGPoint(x: 100 + 75, y: 100 - 50 )
                            uiView.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
                        })

                        UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.7, animations: {
                            uiView.center =  CGPoint(x: 100, y: 100)// how can I make this dynamic
                            uiView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
                        })
                    }, completion: { _ in
                        uiView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
                    })
                }
            }
        }

        extension View {
            func throwAnimation( isAnimating: Binding<Bool>) -> some View {
                modifier(ThrowAnimationViewModifier(isAnimating: isAnimating))
            }
        }
        struct ThrowAnimationViewModifier: ViewModifier {
            @Binding var isAnimating: Bool
            func body(content: Content) -> some View {
                ThrowAnimationWrapper(content: {
                    content
                }, isAnimating: $isAnimating)
            }
        }

How I would like to be able to call it from ContentView

    @State private var isAnimating = false
    var body: some View {
        HStack{
            Image(systemName: "circle.fill")
                .font(.system(size: 65))
                .foregroundColor(.blue)
                .throwAnimation(isAnimating: $isAnimating, startPos:CGPoint(x: 250, y: 300), endPos:CGPoint(x: 100, y: 100))
                .onTapGesture {
                    isAnimating.toggle()
                }
        }
    }
}

How can I modify this code in a way that I can enter the start and end position when assigning to a view?

Thanks!

2      

Hi, This should work:

struct ContentView: View {
    @State private var isAnimating = false
    var body: some View {
        HStack{
            Image(systemName: "circle.fill")
                .font(.system(size: 65))
                .foregroundColor(.blue)
                .throwAnimation(isAnimating: $isAnimating, startPos: CGPoint(x: 250, y: 300), endPos: CGPoint(x: 100, y: 100))
                .onTapGesture {
                    isAnimating.toggle()
                }
        }
    }
}

struct ThrowAnimationWrapper<Content: View>: UIViewRepresentable{
    @ViewBuilder let content: () -> Content
    @Binding var isAnimating: Bool
    var startPos: CGPoint
    var endPos: CGPoint

    func makeUIView(context: Context) -> UIView {
        UIHostingController(rootView: content()).view
    }
    func updateUIView(_ uiView: UIView, context: Context) {
        if isAnimating{
            UIView.animateKeyframes(withDuration: 1.5, delay: 0.0, options: [.calculationModeCubic], animations: {
                UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: {
                    uiView.center =  self.startPos // how can I make this dynamic
                })
                UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.9, animations: {
                    uiView.center =  CGPoint(x: 100 + 75, y: 100 - 50 )
                    uiView.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
                })

                UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.7, animations: {
                    uiView.center =  self.endPos// how can I make this dynamic
                    uiView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
                })
            }, completion: { _ in
                uiView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
            })
        }
    }
}

extension View {
    func throwAnimation(isAnimating: Binding<Bool>, startPos: CGPoint, endPos: CGPoint) -> some View {
        modifier(ThrowAnimationViewModifier(isAnimating: isAnimating, startPos: startPos, endPos: endPos))
    }
}
struct ThrowAnimationViewModifier: ViewModifier {
    @Binding var isAnimating: Bool
    var startPos: CGPoint
    var endPos: CGPoint
    func body(content: Content) -> some View {
        ThrowAnimationWrapper(content: {
            content
        }, isAnimating: $isAnimating, startPos: startPos, endPos: endPos)
    }
}

3      

Thanks a LOT for your help, I appreciate it.

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.