Home > Mobile >  Share background across views
Share background across views

Time:11-04

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).

enter image description here

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)
    }
}
  • Related