open row from List located on second TabItem from first TabItem

Viewed 42

I have got two tabItems in TabView with FirstScreen and PlacesScreen. FirstScreen contains picker with items located in array in the model PlacesViewModel. The same model is used for the list from the second tabItem on PlacesScreen. By choosing a particular item in picker and pressing button, I have to open the second tabItem and open a particular row in the list on PlacesScreen. I have figured out how to open the second tabItem but stuck with opening a particular row.

The first screen code with picker and button:

struct FirstScreen: View {
        
        @EnvironmentObject var placesViewModel: PlacesViewModel
        @State var placeChoice = 0
        @Binding var tabSelection: Int
        
        var body: some View {
            NavigationView {
                Form {
                    VStack {
                        Picker("Places", selection: $placeChoice) {
                            ForEach(0 ..< placesViewModel.places.count) { index in
                                Text(placesViewModel.places[index].name)
                                    .tag(index)
                            }
                        }.pickerStyle(WheelPickerStyle())
                        
                        Button {
                            tabSelection = 1
                        } label: {
                            Text("Show place")
                                .font(.system(size: 20))
                                .padding()
                                .foregroundColor(.white)
                                .background(Color.blue)
                                .cornerRadius(8)
                        }
                    }
                    
                }
                .navigationTitle("First Screen")
            }
            
        }
    }
    
    struct FirstScreen_Previews: PreviewProvider {
        static var previews: some View {
            FirstScreen(tabSelection: .constant(0))
        }
    }

The second screen with a List:

struct PlacesScreen: View {
    var body: some View {
        PlacesList()
    }
}

struct PlacesScreen_Previews: PreviewProvider {
    static var previews: some View {
        PlacesScreen()
    }
}

struct PlacesList: View {
    
    @EnvironmentObject var placesViewModel: PlacesViewModel
    
    var body: some View {
        NavigationView {
            List(placesViewModel.places) { place in
                NavigationLink {
                    PlaceScreen(place: place)
                } label: {
                    HStack {
                        place.image
                            .resizable()
                            .frame(width: 50, height: 50)
                        Text(place.name)
                        
                        Spacer()
                    }
                }
                
            }
            .navigationTitle(placesViewModel.title)
        }
        
    }
}

struct PlacesList_Previews: PreviewProvider {
    static var previews: some View {
        PlacesList()
    }
}

Here is the TabView code:

struct ContentView: View {
    
    @State var tabSelection: Int = 0
    
    var body: some View {
        
        TabView(selection: $tabSelection) {
            FirstScreen(tabSelection: tabSelection)
                .tag(0)
                .tabItem {
                    Label("First Screen", systemImage: "list.bullet.rectangle")
                }
            
            PlacesScreen()
                .tag(1)
                .tabItem {
                    Label("Places", systemImage: "tray.2.fill")
                }
          
        }
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Thank you in advance for your help!

1 Answers

You can use NavigationLink(tag: selection:). Here is a fully functional demo based on your code.

enter image description here

struct ContentView: View {
    
    @State var placeChoice: String?
    @State private var tabSelection: Int = 0
    
    var body: some View {
        TabView(selection: $tabSelection) {
            FirstScreen(placeChoice: $placeChoice, tabSelection: $tabSelection)
                .tabItem {
                    Label("One", systemImage: "mappin.circle.fill")
                }
                .tag(0)

            PlacesList(placeChoice: $placeChoice)
                .tabItem {
                    Label("Two", systemImage: "mappin.circle.fill")
                }
                .tag(1)
        }
    }
}


let places = ["Paris", "Rome", "Berlin", "London", "Madrid", "Oslo"]


struct FirstScreen: View {
    
    @Binding var placeChoice: String?
    @Binding var tabSelection: Int
    
    @State private var pickerChoice: String = "Rome"
    
    var body: some View {
        NavigationView {
            Form {
                VStack {
                    Picker("Places", selection: $pickerChoice) {
                        ForEach(places, id: \.self) { place in
                            Text(place)
                                .tag(place)
                        }
                    }
//                    .pickerStyle(.segmented)
                    .pickerStyle(WheelPickerStyle())
                    
                    Button {
                        placeChoice = pickerChoice
                        tabSelection = 1
                        print("*******", placeChoice)
                    } label: {
                        Text("Show place")
                            .font(.system(size: 20))
                            .padding()
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
                }
                
            }
            .navigationTitle("First Screen")
        }
        
    }
}


struct PlacesList: View {

    @Binding var placeChoice: String?

    var body: some View {
        NavigationView {
            List {
                ForEach(places, id: \.self) { place in
                    NavigationLink(tag: place, selection: $placeChoice) {
                        //PlaceScreen(place: place)
                        Text("Detail screen \(place)")
                    } label: {
                        HStack {
                            //place.image
                            Image("image")
                                .resizable()
                                .frame(width: 50, height: 50)
                            Text(place)

                            Spacer()
                        }
                    }
                }
            }
            .navigationTitle("Places")
        }

    }
}

And this is the version using the new iOS16 NavigationStack:

struct ContentView: View {

    // iOS16 Navigation Path
    @State private var navPath: [String] = []
    @State private var tabSelection: Int = 0
    
    var body: some View {
        TabView(selection: $tabSelection) {
            FirstScreen(navPath: $navPath, tabSelection: $tabSelection)
                .tabItem {
                    Label("One", systemImage: "mappin.circle.fill")
                }
                .tag(0)

            PlacesList(navPath: $navPath)
                .tabItem {
                    Label("Two", systemImage: "mappin.circle.fill")
                }
                .tag(1)
        }
    }
}


let places = ["Paris", "Rome", "Berlin", "London", "Madrid", "Oslo"]


struct FirstScreen: View {
    
    @Binding var navPath: [String]
    @Binding var tabSelection: Int
    
    @State private var pickerChoice: String = "Rome"
    
    var body: some View {
        NavigationStack {
            Form {
                VStack {
                    Picker("Places", selection: $pickerChoice) {
                        ForEach(places, id: \.self) { place in
                            Text(place)
                                .tag(place)
                        }
                    }
                    .pickerStyle(WheelPickerStyle())
                    
                    Button {
                        navPath = [pickerChoice]
                        tabSelection = 1
                        print("*******", navPath)
                    } label: {
                        Text("Show place")
                            .font(.system(size: 20))
                            .padding()
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
                }
                
            }
            .navigationTitle("First Screen")
        }
        
    }
}


struct PlacesList: View {

    @Binding var navPath: [String]
    
    var body: some View {
        NavigationStack(path: $navPath) {
            List {
                ForEach(places, id: \.self) { place in
                    NavigationLink(value: place) {
                        HStack {
                            //place.image
                            Image("image")
                                .resizable()
                                .frame(width: 50, height: 50)
                            Text(place)

                            Spacer()
                        }
                    }
                }
            }
            .navigationTitle("Places")
            .navigationDestination(for: String.self) { place in
                Text("Detail screen \(place)")
            }
        }

    }
}

ADDED: Version with model as EnvironmentObject

class ViewModel: ObservableObject {
    @Published var navPath: [String] = []
    @Published var tabSelection: Int = 0
    
    @Published var places = ["Paris", "Rome", "Berlin", "London", "Madrid", "Oslo"]
}


struct ContentView: View {

    @StateObject var vm = ViewModel()
        
    var body: some View {
        TabView(selection: $vm.tabSelection) {
            FirstScreen()
                .tabItem {
                    Label("Selection", systemImage: "mappin.circle.fill")
                }
                .tag(0)

            PlacesList()
                .tabItem {
                    Label("Places", systemImage: "tray.2.fill")
                }
                .tag(1)
        }
        .environmentObject(vm)
    }
}


struct FirstScreen: View {
    
    @EnvironmentObject var vm: ViewModel
    
    @State private var pickerChoice: String = "Rome"
    
    var body: some View {
        NavigationStack {
            Form {
                VStack {
                    Picker("Places", selection: $pickerChoice) {
                        ForEach(vm.places, id: \.self) { place in
                            Text(place)
                                .tag(place)
                        }
                    }
                    .pickerStyle(WheelPickerStyle())
                    
                    Button {
                        vm.navPath = [pickerChoice]
                        vm.tabSelection = 1
                    } label: {
                        Text("Show place")
                            .font(.system(size: 20))
                            .padding()
                            .foregroundColor(.white)
                            .background(Color.blue)
                            .cornerRadius(8)
                    }
                }
                
            }
            .navigationTitle("First Screen")
        }
        
    }
}


struct PlacesList: View {

    @EnvironmentObject var vm: ViewModel

    var body: some View {
        NavigationStack(path: $vm.navPath) {
            List {
                ForEach(vm.places, id: \.self) { place in
                    NavigationLink(value: place) {
                        HStack {
                            //place.image
                            Image("image")
                                .resizable()
                                .frame(width: 50, height: 50)
                            Text(place)

                            Spacer()
                        }
                    }
                }
            }
            .navigationTitle("Places")
            .navigationDestination(for: String.self) { place in
                Text("Detail screen \(place)")
            }
        }

    }
}
Related