Ranjeet Kumawat
Ranjeet Kumawat

Reputation: 21

Navigation link not working properly after updating to IOS 18 and XCode 16

I have two tabs on the top, in each tab list of cards are shown and each card has a navigation link which is used to navigate to the detail view. It was working properly until I updated XCODE to 16 and IOS to 18. Below is a sample code that I am using in my app.

The navigation link does not navigate anymore after the update. Below is a sample code that I am using in the app.

struct TabSelectionView: View {
    @State var selectedTab: Int = 0
    
    var body: some View {
        ZStack {
            TabView(selection: $selectedTab) {
                ParentView_1()
                    .tabItem {
                        Text("tab1")
                    }
                    .tag(0)
                
                
                ParentView_2()
                    .tabItem {
                        Text("tab2")
                    }
                    .tag(1)
            }
        }
    }
}
struct ParentView_1: View {
    @EnvironmentObject var environmentValue: ViewModel
    
    @State var selectedTab: Int = 0
    var body: some View {
        NavigationStack(path: $environmentValue.navigationPath) {
            VStack{
                Text("Parent 1 ")
                
                TabView(selection: $selectedTab) {
                    Child()
                        .tag(0)
                    
                    Child_2()
                        .tag(1)
                }
                .tabViewStyle(.page(indexDisplayMode: .never))
            }
        }
    }
}
struct ParentView_2: View {
    var body: some View {
        Text("Hello, World!")
    }
}
struct Child: View {
    let dummyItems: [ItemModel] = [
        ItemModel(name: "Home", navigateTo: "HomeView"),
        ItemModel(name: "Profile", navigateTo: "ProfileView"),
        ItemModel(name: "Settings", navigateTo: "SettingsView")
    ]
    
    var body: some View {
        VStack{
            
            Text("Child 1")
            
            ScrollView {
                LazyVStack {
                    ForEach(dummyItems.indices, id: \.self) { index in
                        NavigationLink(value: dummyItems[index]) {
                            RoundedRectangle(cornerRadius: 10)
                                .fill(Color.red.opacity(0.2))
                                .frame(width: 300, height: 100)
                                .overlay {
                                    Text("\(dummyItems[index].name)")
                                }
                        }
                    }
                }
            }
            .navigationDestination(for: ItemModel.self) { navigateValue in
                if navigateValue.navigateTo == "HomeView" {
                    DetailView(name: navigateValue.name)
                } else if navigateValue.navigateTo == "ProfileView" {
                    DetailView_2(name: navigateValue.name)
                } else if navigateValue.navigateTo == "SettingsView" {
                    DetailView_2(name: navigateValue.name)
                }
            }
        }
    }
}


struct Child_2: View {
    let dummyItems: [ItemModel] = [
        ItemModel(name: "Home", navigateTo: "HomeView"),
        ItemModel(name: "Profile", navigateTo: "ProfileView"),
        ItemModel(name: "Settings", navigateTo: "SettingsView")
    ]
    
    var body: some View {
        VStack{
            Text("Child 2")
            
            ScrollView {
                LazyVStack {
                    ForEach(dummyItems.indices, id: \.self) { index in
                        NavigationLink(value: dummyItems[index]) {
                            RoundedRectangle(cornerRadius: 10)
                                .fill(Color.yellow.opacity(0.5))
                                .frame(width: 300, height: 100)
                                .overlay {
                                    Text("\(dummyItems[index].name)")
                                }
                        }
                    }
                }
                .navigationDestination(for: ItemModel.self) { navigateValue in
                    if navigateValue.navigateTo == "HomeView" {
                        DetailView(name: navigateValue.name)
                    }
                    else if navigateValue.navigateTo == "ProfileView" {
                        DetailView_2(name: navigateValue.name)
                    }
                    else if navigateValue.navigateTo == "SettingsView" {
                        DetailView_2(name: navigateValue.name)
                    }
                }
            }
        }
    }
}
struct DetailView: View {
    var name: String
    var body: some View {
        VStack{
            Text("detail view \(name)")
        }
    }
}

struct DetailView_2: View {
    var name: String
    var body: some View {
        VStack{
            Text("detail 2 view \(name)")
        }
    }
}
class ViewModel: ObservableObject {
    @Published var navigationPath = NavigationPath()
}

I've tried removing the TabView inside the NavigationStack and it works as expected. It worked till IOS 17.6, but I haven't been able to found any solution anywere regarding this.

Upvotes: 2

Views: 476

Answers (1)

Sweeper
Sweeper

Reputation: 270758

You should replace the inner TabView with a ScrollView with .paging behaviour.

struct ParentView_1: View {
    @EnvironmentObject var environmentValue: ViewModel
    
    @State var selectedTab: Int? = 0
    var body: some View {
        NavigationStack(path: $environmentValue.navigationPath) {
            VStack{
                Text("Parent 1 ")
                
                ScrollView(.horizontal) {
                    HStack {
                        Child()
                            .containerRelativeFrame(.horizontal)
                            .id(0)
                        
                        Child_2()
                            .containerRelativeFrame(.horizontal)
                            .id(1)
                    }
                    .scrollTargetLayout()
                }
                .scrollPosition(id: $selectedTab)
                .scrollTargetBehavior(.paging)
                .navigationDestination(for: ItemModel.self) { navigateValue in
                    if navigateValue.navigateTo == "HomeView" {
                        DetailView(name: navigateValue.name)
                    } else if navigateValue.navigateTo == "ProfileView" {
                        DetailView_2(name: navigateValue.name)
                    } else if navigateValue.navigateTo == "SettingsView" {
                        DetailView_2(name: navigateValue.name)
                    }
                }
            }
        }
    }
}

Since both Child and Child_2 navigate to the same destinations, you should move the navigationDestination modifier to the parent.

If they need to navigate to different destinations in your real code, they should add navigationDestinations for different model types. For example, in Child you can write

.navigationDestination(for: ItemModel1.self) { ... }

and in Child_2 you can use a different type:

.navigationDestination(for: ItemModel2.self) { ... }

Upvotes: 2

Related Questions