skip to Main Content

I’m new to learning SwiftUI and XCode and am unable to figure out how to pass a variable from view to another. I read on @State and @Binding variables but from what I can tell that is for values that change. I have a static value that I calculate based on the date when the user opens the app.

The variable is the current moon phase and is stored locally in my main ContentView. I want to pass this variable to a second view that’s accessed by clicking a NavigationLink.

ContentView.swift

import SwiftUI

struct ContentView: View {
    
    
    var body: some View {
        
        
        let currentMoonPhaseArray = calculateMoonPhase()
        let moonPhase = currentMoonPhaseArray[0]
        
        NavigationView{
            ScrollView(.vertical, showsIndicators:true) {
                VStack(spacing:3){
                    NavigationLink(destination: MoonPhaseView()){
                        Text("Moon Phase - " + moonPhase)
                    }
                }
            }
        .frame(maxWidth: .infinity)
        .navigationTitle("MySky")
        .navigationBarTitleDisplayMode(.inline)
        }
        
    }
}

MoonPhaseView.swift

import SwiftUI

struct MoonPhaseView: View {
    
    
    var body: some View {
        HStack{
            Text("MoonPhaseView!")
        }
    }
}

struct MoonPhaseView_Previews: PreviewProvider {
    static var previews: some View {
        MoonPhaseView()
    }
}

My goal is to have the calculated moon phase from ContentView.swift be passed to the MoonPhaseView.swift. I believe that bindings are the correct approach from what I’ve read, but all binding implementations seem to be for updating views often.

Thanks for any help or pointers!

2

Answers


  1. You haven’t shown what the type of moonPhase is, so I’m just going to use String as an example.

    
    struct ContentView: View {
        
        func calculateMoonPhase() -> [String] {
            return ["Full","Waxing","Waning"]
        }
        
        var body: some View {
            let currentMoonPhaseArray = calculateMoonPhase()
            let moonPhase = currentMoonPhaseArray[0]
            
            NavigationView{
                ScrollView(.vertical, showsIndicators:true) {
                    VStack(spacing:3){
                        NavigationLink(destination: MoonPhaseView(phase: moonPhase)){
                            Text("Moon Phase - " + moonPhase)
                        }
                    }
                }
                .frame(maxWidth: .infinity)
                .navigationTitle("MySky")
                .navigationBarTitleDisplayMode(.inline)
            }
        }
    }
    
    struct MoonPhaseView: View {
        var phase : String
        
        var body: some View {
            HStack{
                Text("MoonPhaseView!")
                Text(phase)
            }
        }
    }
    
    struct MoonPhaseView_Previews: PreviewProvider {
        static var previews: some View {
            MoonPhaseView(phase: "Full")
        }
    }
    

    Note that every time MoonPhaseView is used, you must provide a phase parameter so that it has a value to fill var phase : String. You could provide a default value, but that doesn’t seem like it would do much good here.


    Not directly related to your question, but I might suggest that calculating the phase in the body might lead to undesirable results, especially if it’s an expensive calculation or it has to contact an API or something. You might want to consider doing this in onAppear and keeping it in a @State variable in your ContentView, or perhaps even using an ObservableObject as a view model and storing the phase there.

    Login or Signup to reply.
  2. You can use "Environment" to pass system-wide settings to views and child views.

    For example:

    @main
    struct TestApp: App {
        let moonPhaseValue = "Waxing"   // <--- some value 
        var body: some Scene {
            WindowGroup {
                ContentView().environment(.moonPhase, moonPhaseValue) // <--- pass it around
            }
        }
    }
    
    
    struct ContentView: View {
        @Environment(.moonPhase) var moonPhase   // <--- the value again
        var body: some View {
            NavigationView {
                VStack {
                    NavigationLink(destination: MoonPhaseView()) {
                        Text("Moon Phase - " + moonPhase)
                    }
                }
            }.navigationViewStyle(StackNavigationViewStyle())
        }
    }
    
    struct MoonPhaseView: View {
        @Environment(.moonPhase) var moonPhase  // <--- the value again
        var body: some View {
            HStack{
                Text("MoonPhaseView is (moonPhase)")
            }
        }
    }
    
    // create your own EnvironmentKey
    struct MoonPhaseKey: EnvironmentKey {
        static let defaultValue: String = "Full"
    }
    // create your own EnvironmentValues
    extension EnvironmentValues {
        var moonPhase: String {
            get { return self[MoonPhaseKey] }
            set { self[MoonPhaseKey] = newValue }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search