In this post, we’ll learn how to build a horizontal menu similar to the one used in the Medium mobile application, like this:
The code is:
struct MenuSwiftUIView: View { var days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] @State var selected = 0 var body: some View { VStack { ScrollView(.horizontal) { HStack(spacing: 0) { ForEach(Array(days.enumerated()), id: \.offset) { index, day in VStack { Text(day).padding(5) .foregroundStyle(.white) if selected == index { Color(.white) .frame(height: 2) } else { Color(.gray) .frame(height: 1) } }.onTapGesture { selected = index } } } }.scrollIndicators(ScrollIndicatorVisibility.hidden) Spacer() // Here what you want show considering the selected value }.padding() .background(.black) } }
We define an array of strings that contains the menu options and a variable selected to store the value of the selected index in the menu.
The menu is built using a ScrollView that scrolls horizontally. In this, we add the Text that shows the label and the bottom line.
The HStack has spacing set to zero to avoid space between the bottom lines.
To have the index of the elements, we create a collection of indices and elements with Array(days.enumerated())
[(offset: 0, element: "Monday"), (offset: 1, element: "Tuesday"), (offset: 2, element: "Wednesday"), (offset: 3, element: "Thursday"), (offset: 4, element: "Friday"), (offset: 5, element: "Saturday"), (offset: 6, element: "Sunday")]
We hide the scrollbar in this way:
.scrollIndicators(ScrollIndicatorVisibility.hidden)
The line (indicator) changes color depending on the selected item.
For the last step, we can extract the ScrollView to create a reusable component:
struct HorizontalMenu: View { var labels: Array<String> = [] var defaultColor = Color.gray var selectedColor = Color.white var textColor = Color.white @Binding var selected: Int var body: some View { ScrollView(.horizontal) { HStack(spacing: 0) { ForEach(Array(labels.enumerated()), id: \.offset) { index, label in VStack { Text(label).padding(5) .foregroundStyle(textColor) if selected == index { selectedColor .frame(height: 2) } else { defaultColor .frame(height: 1) } }.onTapGesture { selected = index } } } }.scrollIndicators(ScrollIndicatorVisibility.hidden) } }
Note that we bind the selected variable because we need to use its value in the main view.
Thus, the code becomes:
struct MenuSwiftUIView: View { var days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] @State var selected = 0 var body: some View { VStack { HorizontalMenu(labels: days, selected: $selected) Spacer() // Here what you want show considering the selected value }.padding() .background(.black) } }
(Blog image from unsplash)