Reputation: 21
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
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 navigationDestination
s 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