Home > Software engineering >  Using EnvironmentObject and Binding in same struct? [Beginner]
Using EnvironmentObject and Binding in same struct? [Beginner]

Time:11-28

I have managed to set up a Navigationstack that I use programatically to insert/remove views. In my FindOrderView, I want to carry over the variable "ordernumber" to my LoadingScreen(View) using Binding, but I get an error saying my LoadingScreen does not conform to type 'Equatable'. What could I be doing wrong, and is there any other efficient way to get this working?

I'm a complete beginner with SwiftUi but have searched for hours before posting this here. Thankful for any help.

NavigationRouter (Class used to control layers of views)

import Foundation
import SwiftUI

enum Route: Hashable {
    case ContentView
    case View1
}


final class NavigationRouter: ObservableObject {
    @Published var navigationPath = NavigationPath()
    
    func pushView(route: Route) {
        navigationPath.append(route)
    }
    
    func popToRootView() {
        navigationPath = .init()
    }
    
    func popToSpecificView(k: Int) {
        navigationPath.removeLast(k)
        
    }

}

Mainapp (App starts here but instantly jumps to MainScreenView())

import SwiftUI

@main
struct AvhamtningApp: App {
    
    @StateObject var router = NavigationRouter()
    
    var body: some Scene {
        WindowGroup {
            NavigationStack(path: $router.navigationPath) {
                MainScreenView()
                    .navigationDestination(for: Route.self) { route in
                        switch route {
                        case .ContentView:
                            MainScreenView()
                        case .View1:
                            FindOrderView()
                        }
                    }
            }.environmentObject(router)
        }
    }
} 

MainScreenView (View with button called "Get order" that carries you over to View1 (FindOrderView))

import SwiftUI

struct MainScreenView: View {
    
    @EnvironmentObject var router: NavigationRouter
    
    var body: some View {
            VStack() {
                Text("Testing")
                    .font(.largeTitle)
                    .fontWeight(.heavy)
                    .foregroundColor(.yellow)
                    .padding(.top, 50)
                
                Spacer()
                PrimaryButton(text: "Get Order")
                    .onTapGesture {
                        router.pushView(route: .View1)
                    }
                Spacer()
                
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color(.white)).ignoresSafeArea(.all)
    }
}

struct MainScreenView_Previews: PreviewProvider {
    static var previews: some View {
        MainScreenView()
    }
}

FindOrderView (View with Keypad, if enter 7 digits then should carry you over to Loadingscreen() with the variable "ordernumber" as binding)

import SwiftUI

var list = [["1","2","3"],["4","5","6"],["7","8","9"],["","0","⌫"]]

struct FindOrderView: View {
    @State private var ordernumber: String = ""
    @EnvironmentObject var router: NavigationRouter
    
    var body: some View {
        
        VStack(spacing: 25) {
            Text(ordernumber)
                .font(.largeTitle)
                .foregroundColor(.black)
                .onChange(of: ordernumber) { newValue in
                    if ordernumber.count >= 7 {
                        router.navigationPath.append(LoadingScreen(ordernumber: $ordernumber))
                    }
                }
            Spacer()
            ForEach(list.indices, id: \.self) { listindex in
                
                HStack{
                    ForEach(list[listindex], id: \.self) { variableindex in
                        Text(variableindex)
                            .foregroundColor(.blue)
                            .font(.system(size: 60))
                            .onTapGesture(perform: {
                                if variableindex == "⌫" && !ordernumber.isEmpty {
                                    ordernumber.removeLast()
                                }
                                else if ordernumber.count >= 7 {return}
                                
                                else if variableindex == "⌫" {
                                    ()
                                }
                                else {
                                    ordernumber  = variableindex
                                }})
                        .frame(maxWidth: .infinity)
                    }
                    
                    }
                }
            
            }.background(.white)
        
        }
    
    }


struct FindOrderView_Previews: PreviewProvider {
    static var previews: some View {
        FindOrderView()
    }
}
 

LoadingScreen (This is where I get the error "Type 'LoadingScreen' does not conform to protocol 'Equatable'")

import SwiftUI

struct LoadingScreen: Hashable, View {
    
    
    @EnvironmentObject var router: NavigationRouter
    @Binding var ordernumber: String
    
    var body: some View {
        ZStack{
            Text("Loading...")
                .font(Font.custom("Baskerville-Bold", size: 50))
                .foregroundColor(.orange)
                .padding(.bottom, 200)
            
            ProgressView()
                .progressViewStyle(CircularProgressViewStyle(tint: .orange))
                .scaleEffect(2)
            

        }.frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color(.white)).ignoresSafeArea(.all)
    }
}


struct LoadingScreen_Previews: PreviewProvider {
    static var previews: some View {
        LoadingScreen(ordernumber: .constant("constant"))
    }
}

CodePudding user response:

So the app starts on the MainScreenView because it's set as the root of the NavigationStack.

Your issue with navigation is that you're trying to send the view as a navigation destination value, but you need to send the route value, and switch on the navigationDestination to generate the view to be pushed.

So in your Route enum, add a route for loading:

case loading(orderNumber: String)

Then in your NavigationLink send that new route:

NavigationLink(value: Route.loadingScreen(orderNumber: orderNumber)

Then in your navigationDestination view modifier:

.navigationDestination(for: Route.self) { route in
    switch route {
        case .ContentView:
            MainScreenView()
        case .View1:
            FindOrderView()
        case .loading(let orderNumber):
            LoadingScreen(orderNumber: orderNumber)
                .environmentObject(router)
        }
}

Hope this helps!

  • Related