I have two issues that would love some help on. I have a landing page, split up between two views. One view shows an image, and another displays two buttons (login and sign up). When navigating to the buttons, I would like to stay on the view and not navigate fully to a new view.
I would also like the background to remain the same as we navigate. I attached a gif of the desired effect (minus the background).
In the code below, I added a ZStack which adds a background modifier, but navigating goes fully to a new view, not remaining on the same page.
extension View {
func animatableGradient(fromGradient: Gradient, toGradient: Gradient, progress: CGFloat) -> some View {
self.modifier(AnimatableGradientModifier(fromGradient: fromGradient, toGradient: toGradient, progress: progress))
}
}
struct AnimatableGradientModifier: AnimatableModifier {
let fromGradient: Gradient
let toGradient: Gradient
var progress: CGFloat = 0.0 //keeps track of gradient change
var animatableData: CGFloat {
get { progress }
set { progress = newValue }
}
func body(content: Content) -> some View {
var gradientColors = [Color]()
for i in 0..<fromGradient.stops.count {
let fromColor = UIColor(fromGradient.stops[i].color)
let toColor = UIColor(toGradient.stops[i].color)
gradientColors.append(colorMixer(fromColor: fromColor, toColor: toColor, progress: progress))
}
return LinearGradient(gradient: Gradient(colors: gradientColors), startPoint: .topLeading, endPoint: .bottomTrailing)
}
func colorMixer(fromColor: UIColor, toColor: UIColor, progress: CGFloat) -> Color {
guard let fromColor = fromColor.cgColor.components else { return Color(fromColor) }
guard let toColor = toColor.cgColor.components else { return Color(toColor) }
let red = fromColor[0] (toColor[0] - fromColor[0]) * progress
let green = fromColor[1] (toColor[1] - fromColor[1]) * progress
let blue = fromColor[2] (toColor[2] - fromColor[2]) * progress
return Color(red: Double(red), green: Double(green), blue: Double(blue))
}
}
struct LandingPage: View {
@AppStorage("signedIn") var signedIn = false
@State private var isPressed = false
@Environment (\.dismiss) var dismiss
@State private var progress: CGFloat = 0
//colors for background on landing page
let gradient1 = Gradient(colors: [.yellow, .orange])
let gradient2 = Gradient(colors: [.yellow, .pink])
@State private var animateGradient = false
@State var scale: CGFloat = 1.0
@State var offsetValue: CGFloat = -60 // << image
@State var isMealJournalTitleShowing = false
@ViewBuilder
var body: some View {
NavigationView{
ZStack{
Rectangle()
.animatableGradient(fromGradient: gradient1, toGradient: gradient2, progress: progress)
.ignoresSafeArea()
VStack{
test()
VStack{
NavigationLink(destination: testb() .navigationBarHidden(true),
label:{
Text("Get Started").fontWeight(.bold)
.frame(minWidth: 0, maxWidth: 200)
.padding(10)
.foregroundColor(.white)
//draw rectange around buttons
.background(
RoundedRectangle(cornerRadius: 20)
.fill(
LinearGradient(
colors: [.orange, .yellow],
startPoint: .topLeading,
endPoint: .bottomTrailing
)))
})
.simultaneousGesture(TapGesture().onEnded{
offsetValue = -125
scale -= 0.50
isMealJournalTitleShowing = true
})
NavigationLink(destination: testb().navigationBarHidden(true), label: {
Text("Login").fontWeight(.semibold)
.frame(minWidth:0, maxWidth: 200)
.padding(10)
.foregroundColor(.black)
.overlay( RoundedRectangle(cornerRadius: 25)
.stroke(Color.gray, lineWidth: 3)
)
})
}
.frame(height: 500)
}
.frame(height: 500)
}
.onAppear {
DispatchQueue.main.async {
withAnimation(.linear(duration: 2.0).repeatForever(autoreverses: true)) {
self.progress = 1.0
}
}
}
.animation(.easeInOut(duration: 0.50), value: offsetValue)
}
}
}
CodePudding user response:
Navigating creates and shows a complete new view, that's why the background changes, of course. Instead of navigating, maybe you could use something like this?
You could play around with transitions like .transition(.slide.combined(with: .opacity))
for example to get the effect you want.
To get the image animating and scaling, you could just lookup .matchedGeometryEffect
.
import SwiftUI
enum ViewState {
case landingPage
case login
case register
case isLoggedIn
}
struct ContentView: View {
@State private var viewState: ViewState = .landingPage
var body: some View {
ZStack {
Color.teal.opacity(0.4)
switch viewState {
case .landingPage:
VStack {
Text("LandingPage")
.transition(.slide)
.padding(50)
Button("Login") {
viewState = .login
}
Button("Register") {
viewState = .register
}
}
case .login:
Text("Login")
.transition(.slide)
case .register:
Text("Register")
.transition(.slide)
case .isLoggedIn:
Text("LoggedIn")
.transition(.slide)
}
}
.animation(.default, value: viewState)
}
}