I know that there is a PickerStyle
protocol, but to the best of my knowledge, no one has really figured out how to create custom picker styles using it yet?
I need two custom pickers, and I'm struggling to get them "just right".
The first is a picker that allows multiple selections. In theory, I already have this, but in practice, there is something a little wonky going on and I can't figure it out.
Background: I'm creating an audiobook library curating app, and I'm trying to implement functionality for the user to make a book series and add books to it.
What I have, code-wise, is this:
struct SelectableBookList: View {
@Binding var selectedBooks: Set<Audiobook>
@EnvironmentObject var database: LibraryDatabase
var body: some View {
List(database.books) { book in
HStack {
if selectedBooks.contains(book) {
Image(systemName: "checkmark")
}
Image(nativeImage: book.cover)
.resizable()
.frame(width: 30, height: 30)
Text(book.title).font(.title3)
Spacer()
Text(book.authorsStringShort).italic()
}
.onTapGesture(perform: {
if selectedBooks.contains(book) {
selectedBooks.remove(book)
} else {
selectedBooks.insert(book)
}
})
.padding(.horizontal)
}
}
}
struct SeriesCreator: View {
@State private var title = ""
@State private var selectedBooks = Set<Audiobook>()
private var selectedBooksArray: [Audiobook] { selectedBooks.map({$0}).removingDuplicates() }
@EnvironmentObject var database: LibraryDatabase
@State private var presentBookSelector = false
var body: some View {
VStack {
Form {
TextField("Series Title", text: $title)
Section(header: Text("Books in Series")) {
Button(action: { presentBookSelector.toggle() }, label: {
HStack {
Image(systemName: "plus")
Text("Add Books")
}
.font(.title2)
})
.sheet(isPresented: $presentBookSelector, content: {
SelectableBookList(selectedBooks: $selectedBooks)
})
List(selectedBooksArray) { book in
Text(book.title)
.italic()
}
.font(.subheadline)
}
}
// snip buttons and other stuff
}
}
}
In the preview, this works exactly as it should.
But when I actually run the app, a strange thing happens. The first time I tap on a book to select it, the list view changes. It's easier to demonstrate, so here is a video: https://user-images.githubusercontent.com/56804260/111397102-b58eca00-867d-11eb-8769-0e5992994d13.mov
I don't really understand why it's doing that and have no idea how to fix it.
My other custom picker view is both simpler and more complex. It appears if the user is attempting to create a series, but one or more series with the same title already exist in the database. I want to present the user with a list of all the it items with matching titles and ask the user
I only need it to allow one selection, but I want it to appear like an alert or action sheet. Since you can't add a picker to an alert or action sheet, I've sort of cheated by making an action sheet with each button one of the options the user can select, but it's just...clumsy.
private func multiMatchAlert(options: [Series]) -> ActionSheet {
var buttons = [ActionSheet.Button]()
for item in options {
buttons.append(.default(Text("\(item.title): \(item.authorsStringShort)"), action: {
selection = nil
presentationMode.wrappedValue.dismiss()
targetSeries = item
presentSeriesView.toggle()
}))
buttons.append(.default(Text("Create new series"), action: {
createOrFetchSeries(disregardDupes: true)
}))
}
return ActionSheet(title: Text("Select a Series"),
message: Text("Select which series to use"),
buttons: buttons)
}
Does anyone have any advice, or links to other tutorials, on how I might accomplish the sort of functionality I'm going for here?