Home > Enterprise >  How can I make a smoothed custom Picker on SwiftUI?
How can I make a smoothed custom Picker on SwiftUI?

Time:01-30

I would like to replicate this picker in swiftUI. In particular, I have a button on the bottom left of the screen and when I click it I would like to show different icons (similar to the image below, but vertically). As soon as I click on one of the choices the button should shrink back to the initial form (circle) with the chosen icon.

When closed:

Closed button

When open:

Open button

I am new to this language and to app in general, I tried with a Pop Up menu, but it is not the desired result, for now I have an horizontal segmented Picker.

CodePudding user response:

You can't do this with the built-in An expanding picker. Initially it appears as a black circle containing a red icon of a speaker with a slash through it. When I tap it, it expands horizontally to also show a white speaker with an exclamation mark next to it, and a white speaker with two sound lines coming out of it. When I tap the middle icon of the speaker-with-exclamation-mark, the other two icons disappear and the picker collapses down to a black circle showing just the speaker-with-exclamation-mark icon. I tap it again and it re-expands to show all three icons. I tap the rightmost icon of the speaker-with-sound-lines and the picker collapses to the black circle, now showing just the speaker-with-sound-lines icon. I tap to expand the picker and select the red speaker-with-slash icon. The picker collapses to a black circle showing just the red icon. The entire animation then repeats indefinitely.

Here's the code:

enum SoundOption {
    case none
    case alertsOnly
    case all
}

struct SoundOptionPicker: View {
    @Binding var option: SoundOption
    @State private var isExpanded = false

    var body: some View {
        HStack(spacing: 0) {
            button(for: .none, label: "volume.slash")
                .foregroundColor(.red)
            button(for: .alertsOnly, label: "speaker.badge.exclamationmark")
                .foregroundColor(.white)
            button(for: .all, label: "volume.2")
                .foregroundColor(.white)
        }
        .buttonStyle(.plain)
        .background {
            Capsule(style: .continuous).foregroundColor(.black)
        }
    }

    @ViewBuilder
    private func button(for option: SoundOption, label: String) -> some View {
        Button {
            withAnimation(.easeOut) {
                if isExpanded {
                    self.option = option
                    isExpanded = false
                } else {
                    isExpanded = true
                }
            }
        } label: {
            Image(systemName: label)
                .fontWeight(.bold)
                .padding(10)
        }
        .frame(width: shouldShow(option) ? buttonSize : 0, height: buttonSize)
        .opacity(shouldShow(option) ? 1 : 0)
        .clipped()
    }

    private var buttonSize: CGFloat { 44 }

    private func shouldShow(_ option: SoundOption) -> Bool {
        return isExpanded || option == self.option
    }
}

struct ContentView: View {
    @State var option = SoundOption.none

    var body: some View {
        ZStack {
            Color(hue: 0.6, saturation: 1, brightness: 0.2)
            SoundOptionPicker(option: $option)
                .shadow(color: .gray, radius: 3)
                .frame(width: 200, alignment: .trailing)
        }
    }
}
  • Related