skip to Main Content

I am new to Swift and SwiftUI and I am practicing with property wrappers. I created a simple app where you can click on either London, New York or Miami. Once you click on either one it will navigate to it’s own screen which will display the name of the city, the current time and the number of covid deaths.

I’m not connecting it to API’s I just want to practice. I spent almost 8 hours trying to find a way for the Cities to have their own properties showing in the second view without me having to create 3 seperate View Models. Each navigation destination show’s London’s properties. I am trying to find a way to fix it. I also created 3 objects/instances from the Identifiable Model. Thank you so much if you can help me.

Heres my View Model:

class CityViewModel: ObservableObject {


@Published var title = "London"
@Published var deaths = "5894"
@Published var time = "8:36"

}

Now here’s my Content View:

struct ContentView: View {
@ObservedObject var cities = CityViewModel()


    
var body: some View {
    NavigationView {
        ZStack {
            Color.black
                .ignoresSafeArea()
        
        VStack {
            
            NavigationLink(
                destination: ToogleView(cityname: self.$cities.title, deaths: self.$cities.deaths, time: self.$cities.time),
                label: {
                    Text("London")
                        .foregroundColor(.white)
                        .bold()
                        .font(.system(size: 30))
                   
                })
            Spacer()
            NavigationLink(
                destination: ToogleView(cityname: self.$cities.title, deaths: self.$cities.deaths, time: self.$cities.time),
                label: {
                    Text("New York")
                        .foregroundColor(.white)
                        .bold()
                        .font(.system(size: 30))
                })

I created a second view for when the Navigationlink is clicked

struct ToogleView: View {

@Binding var cityname:String
@Binding var deaths:String
@Binding var time:String




var body: some View {
    ZStack {
        Color.black
            .ignoresSafeArea()
        VStack {
            Spacer()
            Text(cityname)
                .foregroundColor(.white)
                .font(.system(size: 24))
                .bold()
            Spacer()
            Text(time)
                .foregroundColor(.white)
                .font(.system(size: 18))
            Spacer()
            HStack{
                Image(systemName: "person.3.fill")
                    .foregroundColor(.white)
                Text(deaths)
                    .foregroundColor(.red)
                    .bold()
                
                
                    
                    
            }
        Spacer()
        }
    }
  }
}

2

Answers


  1. It’s showing everywhere the London’s properties because you have hardcoded London’s properties in the ViewModel.

    I would recommend creating a City Model and passing the CityModel you want to show into the ToggleView

    Below example is also very static and need to be adapted if you want to work with dynamic data for Cities

    class CityViewModel: ObservableObject {
        @Published var city: [CityModel] = [
            CityModel(title: "London", deaths: 5894, time: "8:36"),
            CityModel(title: "New York", deaths: 2332, time: "3:36")
        ]
    }
    
    struct CityModel {
        var title: String
        var deaths: Int
        var time: String
    }
    

    contentView

    struct ContentView: View {
        @ObservedObject var cities = CityViewModel()
    
        var body: some View {
            NavigationView {
                ZStack {
                    Color.black
                        .ignoresSafeArea()
    
                    VStack {
    
                        NavigationLink(
                            destination: ToogleView(city: cities.city.first!),
                            label: {
                                Text("London")
                                    .foregroundColor(.white)
                                    .bold()
                                    .font(.system(size: 30))
    
                            })
                        Spacer()
                        NavigationLink(
                            destination: ToogleView(city: cities.city.last!),
                            label: {
                                Text("New York")
                                    .foregroundColor(.white)
                                    .bold()
                                    .font(.system(size: 30))
                            })
                    }
                }
            }
        }
    }
    

    ToggleView

    struct ToogleView: View {
        var city: CityModel
    
        var body: some View {
            ZStack {
                Color.black
                    .ignoresSafeArea()
                VStack {
                    Spacer()
                    Text(city.title)
                        .foregroundColor(.white)
                        .font(.system(size: 24))
                        .bold()
                    Spacer()
                    Text(city.time)
                        .foregroundColor(.white)
                        .font(.system(size: 18))
                    Spacer()
                    HStack{
                        Image(systemName: "person.3.fill")
                            .foregroundColor(.white)
                        Text(String(city.deaths))
                            .foregroundColor(.red)
                            .bold()
                    }
                    Spacer()
                }
            }
        }
    }
    
    Login or Signup to reply.
  2. Well you’re going to need to store the data some where and since you don’t want to use apis and avoid different view models here is an option.

    Create a swift file named City and add this:

      struct City: Identifiable {
        let id = UUID()
        let name: String
        let deaths: Int
        let time: String
    }
    

    then create the instances in the CityViewModel

        let myChosenCities = [City(name: "London", deaths: 3252, time: "8:32"),
                          City(name: "Japan", deaths: 3461, time: "4:21"),
                          City(name: "Canada", deaths: 2352, time: "9:93")
    ]
    

    Then modify your view to accept a city when its initialized like this.

      struct ToogleView: View {
    
        let city: City
    
    var body: some View {
        ZStack {
            Color.black
                .ignoresSafeArea()
            VStack {
                Spacer()
                Text(city.name)
                    .foregroundColor(.white)
                    .font(.system(size: 24))
                    .bold()
                Spacer()
                Text(city.time)
                    .foregroundColor(.white)
                    .font(.system(size: 18))
                Spacer()
                HStack{
                    Image(systemName: "person.3.fill")
                        .foregroundColor(.white)
                    Text(String(city.deaths))
                        .foregroundColor(.red)
                        .bold()
                }
            Spacer()
            }
        }
      }
    }
    

    Then change your parent view to this:

    struct ContentView: View {
        @ObservedObject var vm = CityViewModel()
        
        var body: some View {
            NavigationView {
                ZStack {
                    Color.black
                        .ignoresSafeArea()
                    
                    VStack {
                        List(vm.myChosenCities) { city in
                            NavigationLink(destination: ToogleView(city: city)) {
                            HStack {
                                VStack {
                                    Text(city.name)
                                    Text(city.time)
                                }
                                Text(String(city.deaths))
                            }
                            }
                        }
                    }
                }
            }
        }
    }
    

    And your viewModel to this:

        class CityViewModel: ObservableObject {
    
        
        let myChosenCities = [City(name: "London", deaths: 3252, time: "8:32"),
                              City(name: "Japan", deaths: 3461, time: "4:21"),
                              City(name: "Canada", deaths: 2352, time: "9:93")
        ]
    
    }
    

    I made your example into a list for easy organization but you should be able to see how it all works

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search