skip to Main Content

I’m trying to sort a list using Picker inside a Menu as in the image

enter image description here

the picker is working and sorting is working, the missing part is sorting order

the goal behavior:
when user select a sort field and the sort order will be ASC, but when he select the same field again sort order should be DES.

how can I detect if the user selected the same field again in picker? or is there a better approach.

thank you

here is my example code

struct Book: Identifiable {
    let id = UUID()
    let title, author: String
    let sales: Int
}

enum SortField: String, CaseIterable {
    case title, author, sales
}

struct ContentView: View {
    
    @State private var items: [Book] = [
        .init(title: "Da Vinci Code,The", author: "Brown, Dan", sales: 5094805),
        .init(title: "Harry Potter and the Deathly Hallows", author: "Rowling, J.K.", sales: 4475152),
        .init(title: "Angels and Demons", author: "Brown, Dan", sales: 3193946),
        .init(title: "One Day", author: "Nicholls, David", sales: 1616068),
        .init(title: "Billy Connolly", author: "Stephenson, Pamela", sales: 1231957),
    ]
    
    @State private var selectedSortField: SortField = .title
    @State private var ascending: Bool = false
    
    var body: some View {
        NavigationView {
            Form {
                ForEach(items) { item in
                    HStack {
                        VStack(alignment: .leading) {
                            Text(item.title)
                                .font(.headline)
                            Text(item.author)
                        }
                        Spacer()
                        Text("(item.sales)")
                            .font(.caption)
                    }
                }
            }
            .toolbar {
                Menu {
                    Picker(selection: $selectedSortField, label: Text("Sorting")) {
                        ForEach(SortField.allCases, id: .self) { field in
                            HStack {
                                Text(field.rawValue)
                                if selectedSortField == field {
                                    Image(systemName: ascending ? "arrow.up" : "arrow.down")
                                }
                            }
                        }
                    }
                    .onChange(of: selectedSortField) { _ in sort() }
                } label: {
                    Label("Menu", systemImage: "ellipsis.circle")
                }
            }
        }
    }
    
    private func sort() {
        switch selectedSortField {
        case .title: items.sort { ascending ? $0.title < $1.title : $0.title > $1.title }
        case .author: items.sort { ascending ? $0.author < $1.author : $0.author > $1.author }
        case .sales: items.sort { ascending ? $0.sales < $1.sales : $0.sales > $1.sales }
        }
    }
    
}

2

Answers


  1. Chosen as BEST ANSWER

    this is another solution to this problem

    struct Book: Identifiable {
        let id = UUID()
        let title, author: String
        let sales: Int
    }
    
    enum SortField: String, CaseIterable {
        case title, author, sales
    }
    
    struct ContentView: View {
        
        @State private var items: [Book] = [
            .init(title: "Da Vinci Code,The", author: "Brown, Dan", sales: 5094805),
            .init(title: "Harry Potter and the Deathly Hallows", author: "Rowling, J.K.", sales: 4475152),
            .init(title: "Angels and Demons", author: "Brown, Dan", sales: 3193946),
            .init(title: "One Day", author: "Nicholls, David", sales: 1616068),
            .init(title: "Billy Connolly", author: "Stephenson, Pamela", sales: 1231957),
        ]
        
        @State private var selectedSortField: SortField = .title
        @State private var ascending: Bool = false
        
        var body: some View {
            NavigationView {
                Form {
                    ForEach(items) { item in
                        HStack {
                            VStack(alignment: .leading) {
                                Text(item.title)
                                    .font(.headline)
                                Text(item.author)
                            }
                            Spacer()
                            Text("(item.sales)")
                                .font(.caption)
                        }
                    }
                }
                .toolbar {
                    Menu {
                        ForEach(SortField.allCases, id: .self) { field in
                            Toggle(isOn: .init(get: {
                                return selectedSortField == field
                            }, set: { value in
                                if selectedSortField == field {
                                    ascending.toggle()
                                }
                                selectedSortField = field
                                sort()
                            })) {
                                Label(field.rawValue, systemImage: selectedSortField == field ? (ascending ? "arrow.up" : "arrow.down") : "")
                            }
                        }
                    } label: {
                        Label("Menu", systemImage: "ellipsis.circle")
                    }
                }
            }
        }
        
        private func sort() {
            switch selectedSortField {
            case .title: items.sort { ascending ? $0.title < $1.title : $0.title > $1.title }
            case .author: items.sort { ascending ? $0.author < $1.author : $0.author > $1.author }
            case .sales: items.sort { ascending ? $0.sales < $1.sales : $0.sales > $1.sales }
            }
        }
        
    }
    

  2. A way to achieve this would be to include the ascending flag in the SortField cases. e.g.

    struct Book: Identifiable {
        let id = UUID()
        let title, author: String
        let sales: Int
    }
    
    enum SortField: Hashable {
        case title(asc: Bool), author(asc: Bool), sales(asc: Bool)
        
        var title: String {
            switch self {
            case .title: return "Title"
            case .author: return "Author"
            case .sales: return "Sales"
            }
        }
        
        var isAscending: Bool {
            switch self {
            case .title(let asc), .author(let asc), .sales(let asc):
                return asc
            }
        }
        
        var inverse: Self {
            switch self {
            case .title(let asc):
                return .title(asc: !asc)
            case .author(let asc):
                return .author(asc: !asc)
            case .sales(let asc):
                return .sales(asc: !asc)
            }
        }
    }
    
    struct ContentView: View {
        
        @State private var items: [Book] = [
            .init(title: "Da Vinci Code,The", author: "Brown, Dan", sales: 5094805),
            .init(title: "Harry Potter and the Deathly Hallows", author: "Rowling, J.K.", sales: 4475152),
            .init(title: "Angels and Demons", author: "Brown, Dan", sales: 3193946),
            .init(title: "One Day", author: "Nicholls, David", sales: 1616068),
            .init(title: "Billy Connolly", author: "Stephenson, Pamela", sales: 1231957),
        ]
        
        @State private var selectedSortField: SortField = .title(asc: true)
        
        var body: some View {
            NavigationView {
                Form {
                    ForEach(items) { item in
                        HStack {
                            VStack(alignment: .leading) {
                                Text(item.title)
                                    .font(.headline)
                                Text(item.author)
                            }
                            Spacer()
                            Text("(item.sales)")
                                .font(.caption)
                        }
                    }
                }
                .toolbar {
                    Menu {
                        Picker(selection: $selectedSortField, label: Text("Sorting")) {
                            ForEach(sortFields, id: .self) { field in
                                HStack {
                                    Text(field.title)
                                    if selectedSortField == field || selectedSortField == field.inverse {
                                        Image(systemName: field.isAscending ? "arrow.up" : "arrow.down")
                                    }
                                }
                            }
                        }
                        .onChange(of: selectedSortField) { _ in sort() }
                    } label: {
                        Label("Menu", systemImage: "ellipsis.circle")
                    }
                }
            }
        }
        
        var sortFields: [SortField] {
            var sortFields: [SortField] = []
            if case .title(true) = selectedSortField {
                sortFields.append(.title(asc: false))
            } else {
                sortFields.append(.title(asc: true))
            }
            if case .author(true) = selectedSortField {
                sortFields.append(.author(asc: false))
            } else {
                sortFields.append(.author(asc: true))
            }
            if case .sales(true) = selectedSortField {
                sortFields.append(.sales(asc: false))
            } else {
                sortFields.append(.sales(asc: true))
            }
            return sortFields
        }
        
        private func sort() {
            switch selectedSortField {
            case .title(let ascending): items.sort { ascending ? $0.title < $1.title : $0.title > $1.title }
            case .author(let ascending): items.sort { ascending ? $0.author < $1.author : $0.author > $1.author }
            case .sales(let ascending): items.sort { ascending ? $0.sales < $1.sales : $0.sales > $1.sales }
            }
        }
    }
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search