Multi Select Picker for SwiftUI
I was looking for ability to select multiple items. During my exploration I have found that there is no possiblity to make it select more than one item. This fact was also confirmed by Paul Hudson on his #Slack channel. Being in this situation I searched for any examples of such code.
This led me to Stackoverflow where I have found partially working solution. So after many refinements and tweaks I managed to simulate Picker and be able to select multiple items.
Below is complete working code with example Language Enum as source of choices. When you select “Choose languages” selection sheet is presented. When you select 0 or more item and hit “OK” button modal is dismissed and counter on “Choose languages” row is updated to number of items choosen.
This code is very rough, and there are many places where it could be written better, but this is its first implementation which works as I wanted.
If you have any comments about below elements feel free to comment on Twitter.
This is main view which will present Picker:
ContentView
import Combine
import SwiftUI
struct ContentView: View {
@State private var showLanguageSheet = false
@State private var x = 0
@ObservedObject var preferedLanguages = PreferedLanguages()
var body: some View {
NavigationView {
VStack {
Form {
Section(header: Text("Language").font(.caption)) {
Button(action: {
self.showLanguageSheet.toggle()
}) {
HStack {
Text("Choose languages").foregroundColor(Color.black)
Spacer()
Text("\(preferedLanguages.languages.count)")
.foregroundColor(Color(UIColor.systemGray))
.font(.body)
Image(systemName: "chevron.right")
.foregroundColor(Color(UIColor.systemGray4))
.font(Font.body.weight(.medium))
}
}
.sheet(isPresented: $showLanguageSheet) {
SettingsLanguagePickerView(self.preferedLanguages)
}
Picker(selection: $x, label: Text("One item Picker")) {
ForEach(0..<10) { x in
Text("\(x)")
}
}
NavigationLink(destination: self) {
Text("Default navigation link")
}
}
}
}
.navigationBarTitle("Content")
}
}
}
Source of Picker items - in this example Language enum
Language Enum
import Combine
import SwiftUI
enum Language: Int, CaseIterable, Identifiable {
case english = 0
case polish
var id: Language {
self
}
var literal: String {
switch self {
case .english: return "English"
case .polish: return "Polish"
}
}
}
Combine helper which passes updated values between views and updates picker with previously choosen values
Combine Helper class to expose selected language array
class PreferedLanguages: ObservableObject {
@Published var languages = [Language]()
}
Main Picker View
Multiple Select Picker
struct SettingsLanguagePickerView: View {
@State private var selections = [Language]()
@ObservedObject var preferedLanguages: PreferedLanguages
init(_ preferedLanguages: PreferedLanguages) {
self.preferedLanguages = preferedLanguages
}
@Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
List {
Section(header: Text("Choose prefered languages")) {
ForEach(Language.allCases) { item in
MultipleSelectionRow(title: item.literal, isSelected: self.selections.contains(item)) {
if self.selections.contains(item) {
self.selections.removeAll(where: { $0 == item })
}
else {
self.selections.append(item)
}
}
}
}
}
.onAppear(perform: { self.selections = self.preferedLanguages.languages })
.listStyle(GroupedListStyle())
.navigationBarTitle("Languages", displayMode: .inline)
.navigationBarItems(trailing:
Button(action: {
self.preferedLanguages.languages = self.selections
self.presentationMode.wrappedValue.dismiss()
}) {
Text("OK")
}
)
}
}
}
Picker Row
Multiple select row
struct MultipleSelectionRow: View {
var title: String
var isSelected: Bool
var action: () -> Void
var body: some View {
Button(action: self.action) {
HStack {
Text(self.title)
if self.isSelected {
Spacer()
Image(systemName: "checkmark").foregroundColor(.blue)
}
}
}.foregroundColor(Color.black)
}
}
Credits for base implementation of Multiple Select Picker goes to graycampbell and his implementation which I have found at StackOverflow: https://stackoverflow.com/a/57023746/1285959.
This article was mentioned in:
- | Paweł Madej
Licensing: Content Code Apache 2.0