TruMan1
TruMan1

Reputation: 36088

How to build sort direction in menu like Files app?

I'm displaying sort options in my Menu, but I'd also like the user to control the sort direction in the way the Files app works: tapping a second time toggles the sort direction.

enter image description here

Here's what I have:

@State private var selectedSort: SortOption = .name
@State private var isSortAscending = true

enum SortOption {
    case name
    case number
    case length
}

Menu {
    Picker(selection: $selectedSort, label: Text("Sorting options")) {
        Button {
            isSortAscending.toggle()
        } label: {
            HStack {
                Text("Name")
                Spacer()
                Image(systemName: isSortAscending ? "chevron.down" : "chevron.up")
            }
        }
        .tag(SortOption.name)
        Button {
            isSortAscending.toggle()
        } label: {
            HStack {
                Text("Number")
                Spacer()
                Image(systemName: isSortAscending ? "chevron.down" : "chevron.up")
            }
        }
        .tag(SortOption.number)
        Button {
            isSortAscending.toggle()
        } label: {
            HStack {
                Text("Length")
                Spacer()
                Image(systemName: isSortAscending ? "chevron.down" : "chevron.up")
            }
        }
        .tag(SortOption.length)
    }
}

Tapping doesn't toggle the sort state at all. Is there a better or more supported way to do this?

Upvotes: 1

Views: 375

Answers (1)

Asperi
Asperi

Reputation: 257711

Item selected is handled internally, so we need selection side-effect. It is possible to do by injecting will-set side effect in computable binding.

Here is a possible approach tested with Xcode 13.4 / iOS 15.5

demo

Main part:

var sorting: Binding<SortOption> { .init(
    get: { self.selectedSort },
    set: {
        if self.selectedSort == $0 {
            self.isSortAscending.toggle()
        }
        self.selectedSort = $0
    }
)}

var body: some View {
    Menu("Sort") {
        Picker(selection: sorting, label: Text("Sorting options")) {
            ForEach(SortOption.allCases) { option in
                HStack {
                    Text(option.rawValue)
                    Spacer()
                    if selectedSort == option {
                        Image(systemName: isSortAscending ? "chevron.down" : "chevron.up")
                    }

Test module on GitHub

Upvotes: 3

Related Questions