I'm trying to create an app in which there are multiple views that can be transitioned between with animations within them. So far this is done by animating the offset of a VStack inside a scrollView where the offset is calculated by multiplying the screen height by the index of the current screen. I decided to go this route instead of using scrollTo as I wanted to customise the transition animation to ease in and out.
Here is the ContentView code:
import SwiftUI
struct ContentView: View {
@Namespace var carousel
@Namespace var splash
@Namespace var home
@State private var activeIndex = CGFloat(0)
var body: some View {
GeometryReader { geo in
ScrollViewReader { proxy in
ScrollView([]) {
VStack (spacing: 0) {
ZStack {
Color.red
Splash(home: home, geo: geo, proxy: proxy)
.frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
.id(splash)
}
ZStack {
Color.blue
VStack {
Home(activeIndex: self.$activeIndex, splash: splash, home: home, geo: geo, proxy: proxy)
.frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
.offset(y: -200)
.id(home)
}
}
}
.offset(y: activeIndex > 0 ? -geo.size.height * activeIndex + 200 : -geo.size.height * activeIndex)
.animation(.easeInOut(duration: 0.8))
.id(carousel)
}
.frame(width: geo.size.width, height: geo.size.height)
}
}.ignoresSafeArea()
}
}
And here is the code in which the button exists:
import SwiftUI
struct Home: View {
@Binding var activeIndex: CGFloat
var splash: Namespace.ID
var home: Namespace.ID
var geo: GeometryProxy
var proxy: ScrollViewProxy
var body: some View {
VStack (alignment: .leading, spacing: 0) {
HStack {
Spacer()
VStack {
Button(activeIndex == 0 ? "Let's go" : "Go Back") {
if activeIndex == 0 {
self.activeIndex = 1;
}
else {
self.activeIndex = 0;
}
}
.frame(width: 200, height: 50)
.font(.custom("Montserrat-SemiBold", size: 18))
.background(Color.green)
.cornerRadius(25)
.foregroundColor(.white)
.offset(y: 25)
}
Spacer()
}
.zIndex(1)
VStack {
}
.frame(width: geo.size.width, height: geo.size.height * 0.8, alignment: .leading)
.background(Color.white)
.cornerRadius(40)
}
.frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
.cornerRadius(40)
}
}
Here's a gif of the functionality so far
I want to animate the 'Let's Go' button on the home page to wiggle slightly, then when it's pressed, the animation stops as the screen transition begins. However, this is not as easy as I thought, as this animation seems to be affected by the animation on the VStack in ContentView.
Here is my attempt at making this animation work:
import SwiftUI
struct Home: View {
@State private var isAnimating = false
@State private var stopAnimating = false
@Binding var activeIndex: CGFloat
var splash: Namespace.ID
var home: Namespace.ID
var geo: GeometryProxy
var proxy: ScrollViewProxy
var body: some View {
VStack (alignment: .leading, spacing: 0) {
HStack {
Spacer()
VStack {
Button(activeIndex == 0 ? "Let's go" : "Go Back") {
if activeIndex == 0 {
self.activeIndex = 1;
}
else {
self.activeIndex = 0;
}
isAnimating.toggle()
stopAnimating.toggle()
}
.frame(width: 200, height: 50)
.font(.custom("Montserrat-SemiBold", size: 18))
.background(Color.green)
.cornerRadius(25)
.foregroundColor(.white)
.offset(y: 25)
.shadow(color: Color.black.opacity(0.35), radius: 10)
.rotationEffect(Angle(degrees: self.isAnimating ? 5 : self.stopAnimating ? 0 : -5))
.animation(self.isAnimating ? Animation // Animation added
.easeInOut(duration: 0.2)
.repeatForever() : .default, value: self.isAnimating)
.onAppear { self.isAnimating = true }
}
Spacer()
}
.zIndex(1)
VStack {
}
.frame(width: geo.size.width, height: geo.size.height * 0.8, alignment: .leading)
.background(Color.white)
.cornerRadius(40)
}
.frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
.cornerRadius(40)
}
}
And here is the (hilarious) output
Any help would be much appreciated!
