Reputation: 692
Is there an existing solution to get a horizontal picker in Swift UI?
Example for Swift: https://github.com/akkyie/AKPickerView-Swift
Upvotes: 3
Views: 7648
Reputation: 195
Or try this, maybe simple:
struct HorizontalScrollablePicker: View {
@State private var selection: Int = 0
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: true) {
HStack {
Picker("", selection: $selection) {
ForEach(0 ..< 4, id: \.self) { _ in
Label(
title: { Text("All") },
icon: { Image(systemName: "pencil") }
)
.labelStyle(.titleOnly)
.padding()
}
}
.padding()
.frame(width: .infinity)
.pickerStyle(.palette)
}
.frame(
minWidth: geometry.size.width,
minHeight: geometry.size.height
)
}
}
}
}
Result:
Upvotes: 0
Reputation: 195
First create a container:
struct HorizontalScrollViewContainer<Content>: View where Content: View {
@ViewBuilder private(set) var content: Content
var body: some View {
ScrollViewReader { _ in
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack {
content
}
.frame(minWidth: geometry.size.width, maxWidth: .infinity)
}
}
}
}
2)Create a your item implementation: for example:
struct HorizontalScrollViewItem: View {
@State private var items: [String] = ["All Items", "Math Symbol", "Brackets"]
@State private var selected: String = String.Empty
var body: some View {
ForEach($items, id: \.self) { item in
Button {
$selected.wrappedValue = item.wrappedValue
} label: {
Image(systemName: "applelogo")
.padding(6)
.font(.title2)
Text(item.wrappedValue)
.font(.title3)
}
.withButtonStyle(
(item.wrappedValue == selected) ? .borderedProminent : .borderless
)
.padding(12)
}
}
Wrapp-Up:
struct HorizontalScrollView: View {
var body: some View {
HorizontalScrollViewContainer {
HorizontalScrollViewItem()
}
}
}
Upvotes: 0
Reputation: 1
I used the answer below, and it worked for me. I also made some modifications to make the ScrollView look like a real horizontal picker with animation, using ScrollViewReader. Here is the code, which you can change as you need it.
ScrollViewReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 0) {
ForEach(0..<viewModel.items.count, id: \.self) { i in
Text(viewModel.months[i])
.font(.system(size: 20, weight: .medium))
.foregroundColor(self.selectedItem == i ? .white : .accent)
.gesture(TapGesture().onEnded({
withAnimation(.linear) {
proxy.scrollTo(i, anchor: .center)
selectedItem = i
}
}))
.id(i)
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(i == selectedItem ? .accent : .clear)
)
}
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation(.spring(duration: 2)) {
proxy.scrollTo(selectedItem, anchor: .center)
}
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical)
Upvotes: 0
Reputation: 645
You can use the Rotation Effect on a Picker and on the Picker's content. Make sure to rotate the content 90 degrees opposite from the picker or else the content will be sideways. If the picker is too big, you can manually set a height of the picker by using frame(height: _)
. In my case, I used frame(maxHeight: _)
. You might need to adjust the row indicators by using clipped()
after the resize to stop them from flowing out of the picker.
I'm using images as an example but it should work with most if not all basic views.
Code:
Picker(selection: $data, label: Text("Data")) {
ForEach(dataArray, id: \.self) { imageName in
Image(imageName)
.resizable()
.scaledToFit()
.rotationEffect(Angle(degrees: 90))
}
}
.labelsHidden()
.rotationEffect(Angle(degrees: -90))
.frame(maxHeight: 100)
.clipped()
Upvotes: 6
Reputation: 692
I ended up solving this using a horizontal scrollview, tap gestures, and state to track the selection.
@State private var index
var body: some View {
return ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(0..<self.items.count, id: \.self) { i in
Text("\(i)")
.foregroundColor(self.index == i ? .red : .black)
.frame(width: 20, height: 20)
.gesture(TapGesture().onEnded({ self.index = i }))
}
}
}
.frame(width: self.width, alignment: .leading)
}
Upvotes: 5