I have a NavigationStack
where I have a TabView
and its style as page style and a List
after the TabView
.
Inside the TabView
I have another custom view that has a horizontal List
and I want to remove the space between the the inner child and the parent.
I tried with padding
, plain list style but the space is still there.
I can use frame
modifier but I don't want a hard coded value for the height.
Here is the code I tried:
import SwiftUI
struct CustomView: View {
var dataArray: [String]
var body: some View {
List {
LazyHStack {
ForEach(dataArray, id: \.self) { item in
Button("\(item)") {
}
.buttonStyle(.bordered)
}
}
}
.scrollDisabled(true)
}
}
struct ContentView: View {
struct Sea: Hashable, Identifiable {
let name: String
let id = UUID()
}
struct OceanRegion: Identifiable {
let name: String
let seas: [Sea]
let id = UUID()
}
private let oceanRegions: [OceanRegion] = [
OceanRegion(name: "Pacific",
seas: [Sea(name: "Australasian Mediterranean"),
Sea(name: "Philippine"),
Sea(name: "Coral"),
Sea(name: "South China")]),
OceanRegion(name: "Atlantic",
seas: [Sea(name: "American Mediterranean"),
Sea(name: "Sargasso"),
Sea(name: "Caribbean")]),
OceanRegion(name: "Indian",
seas: [Sea(name: "Bay of Bengal")]),
OceanRegion(name: "Southern",
seas: [Sea(name: "Weddell")]),
OceanRegion(name: "Arctic",
seas: [Sea(name: "Greenland")])
]
@State private var singleSelection: UUID?
@State private var weekIndex = 0
@State private var weeks = [ -1, 0, 1 ]
@State private var dataArray1 = ["1","2","3","4","5","6","7"]
@State private var dataArray2 = ["8","9","10","11","12","13","14"]
@State private var dataArray3 = ["15","16","17","18","19","20","21"]
var body: some View {
NavigationStack {
VStack {
TabView(selection: $weekIndex) {
CustomView(dataArray: dataArray1).tag(weeks[0])
CustomView(dataArray: dataArray2).tag(weeks[1])
CustomView(dataArray: dataArray3).tag(weeks[2])
}
.tabViewStyle(.page(indexDisplayMode: .never))
.indexViewStyle(.page(backgroundDisplayMode: .never))
}
List(selection: $singleSelection) {
ForEach(oceanRegions) { region in
Section(header: Text("Major \(region.name) Ocean Seas")) {
ForEach(region.seas) { sea in
NavigationLink(destination: EmptyView()) {
Text(sea.name)
}
}
}
}
}
.navigationTitle("Oceans and Seas")
.toolbar {
ToolbarItem {
Button(action: {}) {
Label("Add", systemImage: "plus")
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here is a screenshot:
Is there a way to remove that empty space?
How can I expand the list inside the tab view to take all the width and zero padding at the top?
How can I set TabView
's height dynamically, to take its child height?
As Benzy mentioned the TabView
used inside NavigationStack
is not recommended so I finaally found an alternative that works really well.
I used ScrollViewReader
and ScrollView(.horizontal)
together with some modifiers like .containerRelativeFrame(.horizontal)
, .scrollTargetLayout()
and .scrollTargetBehavior(.paging)
. Other modifiers can be used to get the scroll position for example.
It is taking the minimum space and is swipeable.
The ScrollViewReader
is not entirely necessary if the start is at index 0 but if the initial display is not index 0 then it is necessary to preset to which index to scrollTo
. Also this can be controlled using a button for example.
Here is my code:
import SwiftUI
struct CustomView: View {
var dataArray: [String]
var body: some View {
HStack {
ForEach(dataArray, id: \.self) { item in
Button("\(item)") {
print("\(item)")
}
.buttonStyle(.bordered)
}
}
}
}
struct ContentView: View {
struct Sea: Hashable, Identifiable {
let name: String
let id = UUID()
}
struct OceanRegion: Identifiable {
let name: String
let seas: [Sea]
let id = UUID()
}
private let oceanRegions: [OceanRegion] = [
OceanRegion(name: "Pacific",
seas: [Sea(name: "Australasian Mediterranean"),
Sea(name: "Philippine"),
Sea(name: "Coral"),
Sea(name: "South China")]),
OceanRegion(name: "Atlantic",
seas: [Sea(name: "American Mediterranean"),
Sea(name: "Sargasso"),
Sea(name: "Caribbean")]),
OceanRegion(name: "Indian",
seas: [Sea(name: "Bay of Bengal")]),
OceanRegion(name: "Southern",
seas: [Sea(name: "Weddell")]),
OceanRegion(name: "Arctic",
seas: [Sea(name: "Greenland")])
]
@State private var singleSelection: UUID?
@State private var weekIndex: Int?
@State private var weeks = [ -1, 0, 1 ]
@State private var dataArray1 = ["1","2","3","4","5","6","7"]
@State private var dataArray2 = ["8","9","10","11","12","13","14"]
@State private var dataArray3 = ["15","16","17","18","19","20","21"]
var body: some View {
NavigationStack {
ScrollViewReader { value in
ScrollView(.horizontal) {
HStack {
CustomView(dataArray: dataArray1)
.containerRelativeFrame(.horizontal)
.id(weeks[0])
CustomView(dataArray: dataArray2)
.containerRelativeFrame(.horizontal)
.id(weeks[1])
CustomView(dataArray: dataArray3)
.containerRelativeFrame(.horizontal)
.id(weeks[2])
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.paging)
.scrollIndicators(.hidden)
.onAppear {
value.scrollTo(weeks[1])
}
.scrollPosition(id: $weekIndex)
}
List(selection: $singleSelection) {
ForEach(oceanRegions) { region in
Section(header: Text("Major \(region.name) Ocean Seas")) {
ForEach(region.seas) { sea in
NavigationLink(destination: EmptyView()) {
Text(sea.name)
}
}
}
}
}
.navigationTitle("Oceans and Seas")
.toolbar {
ToolbarItem {
Button(action: {}) {
Label("Add", systemImage: "plus")
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Thanks everyone!