swiftswiftuiscrollviewhstack

How to make HStack fill its parent view's width (Scroll View)


I created a tabBar including 2 tabs. It has a horizontal scrollBar which includes a HStack view. But the HStack view only takes a part of screen width as screenshot in below. I used .frame(maxWidth: .infinity), however it changed nothing.

What I want is to make HStack view takes all the screen width size. Please see the 2nd screenshot in below.

struct ContentView: View {
    @State private var currentTab: Int = 0
    
    var body: some View {
        ZStack(alignment: .top) {
            TabView(selection: $currentTab) {
                TextClipView().tag(0)
                BookView().tag(1)
            }
            .tabViewStyle(.page(indexDisplayMode: .never))
            .ignoresSafeArea(.all)
            
            TabBarView(currentTab: $currentTab)
        }
    }
}

struct TabBarView: View {
    @Binding var currentTab: Int
    @Namespace var namespace
    
    var tabBarOptions: [String] = ["text.bubble", "book"]
    
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                ForEach(Array(zip(tabBarOptions.indices, tabBarOptions)),
                        id: \.0,
                        content: {
                    index, name in
                    TabBarItem(currentTab: $currentTab, 
                               namespace: namespace,
                               tabBarItemName: name,
                               tab: index)
                })
            }
            .padding(.horizontal)
            .frame(maxWidth: .infinity)
            .border(.red, width: 4)
        }
        .frame(height: 80)
        .border(.green, width: 2)
        .ignoresSafeArea(.all)
    }
}

struct TabBarItem: View {
    @Binding var currentTab: Int
    let namespace: Namespace.ID
    
    var tabBarItemName: String
    var tab: Int
    
    var body: some View {
        Button {
            currentTab = tab
        } label: {
            VStack {
                Spacer()
                Image(systemName: tabBarItemName)
                    .font(.title)
                if currentTab == tab {
                    Color.black
                        .frame(height: 2)
                        .matchedGeometryEffect(id: "underline",
                                               in: namespace,
                                               properties: .frame)
                } else {
                    Color.clear.frame(height: 2)
                }
            }
            .animation(.spring(), value: currentTab)
        }
        .buttonStyle(.plain)
    }
}


#Preview {
    ContentView()
}

enter image description here enter image description here


Solution

  • Presumably, you are using a ScrollView because there might be times when the width of the content exceeds the available width.

    To make the content fill the Scrollview when its ideal width is smaller, you can use a GeometryReader to measure the width and then set this as minWidth on the HStack.

    Like this:

    // TabBarView
    
    var body: some View {
        GeometryReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 20) {
                    // content as before
                }
                .padding(.horizontal)
                .frame(minWidth: proxy.size.width)
                .border(.red, width: 4)
            }
        }
        .frame(height: 80)
        .border(.green, width: 2)
        .ignoresSafeArea(.all)
    }