blub
blub

Reputation: 692

How to get a horizontal picker for Swift UI?

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

Answers (5)

Christian Chiama
Christian Chiama

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:

Horizontal Scroolable Picker

Upvotes: 0

Christian Chiama
Christian Chiama

Reputation: 195

  1. 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)
    }
}
  1. Wrapp-Up:

    struct HorizontalScrollView: View { 
       var body: some View {
           HorizontalScrollViewContainer {
               HorizontalScrollViewItem()
        }
      }
    

    }

Upvotes: 0

Ismoil Xolmatov
Ismoil Xolmatov

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

James Castrejon
James Castrejon

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

blub
blub

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

Related Questions