I have a Picker that uses an enum for its selection variable. I have designated the selection variable as Equatable, but I get an error when I use the variable with onChange. Here’s the code:
//
// ItemsView.swift
// Bill & Task Manager
//
// Created by Peter Lewis on 1/11/21.
//
import SwiftUI
enum Filter: Equatable { case all, bills, tasks }
struct ItemsView: View
{
@AppStorage("showFilter") var showFilter = false
@EnvironmentObject var periodicals: Periodicals
@State private var showingSearchBar = false
@State private var today = Date.now
@State private var totalPastDue = 0.0
@State private var searchText = ""
@State private var filter = Filter.all
let addButtonImage = Image(systemName: "plus")
let undoButtonImage = Image(systemName: "arrow.uturn.backward")
let searchButtonImage = Image(systemName: "magnifyingglass")
let searchButtonImage2 = Image(systemName: "magnifyingglass.circle.fill")
var title: String
{
switch(filter)
{
case .bills: return "Bills"
case .tasks: return "Tasks"
case .all: return "Bills & Tasks"
}
}
var dateLine: some View
{
let formattedDate = Text(today, style: .date)
return Text("Today is \(formattedDate)")
.padding(7)
.foregroundColor(colorEmphasis)
.background(colorContrast)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
var body: some View
{
NavigationStack. // type '()' cannot conform to 'Equatable'
{
ZStack
{
color1.ignoresSafeArea()
VStack
{
dateLine
if periodicals.items.isEmpty
{
Spacer()
HStack
{
Text("Tap")
addButtonImage.font(.largeTitle)
Text(", above, to add an item.")
}
Spacer()
}
else
{
if showFilter
{
Picker("Filter", selection: $filter)
{
Text("All").tag(Filter.all)
Text("Bills").tag(Filter.bills)
Text("Tasks").tag(Filter.tasks)
}
.pickerStyle(SegmentedPickerStyle())
.fixedSize()
.background(color2)
.cornerRadius(5)
}
List
{
ForEach(filteredSearchResults)
{ item in
NavigationLink(destination: EditView(item: item))
{ ItemRow(item: item) }
.listRowInsets(EdgeInsets(top: 0, leading: 10, bottom: 3, trailing: 0))
.listRowBackground(color2)
.padding([.top, .bottom], 2)
}
.onDelete(perform: removeItem)
.listRowSeparatorTint(.black)
}
.listStyle(.plain)
.cornerRadius(10)
.if(showingSearchBar)
{ view in view.searchable(text: $searchText, prompt: "Search Name") }
SummaryView(total: periodicals.dueInXDays)
PastDueView(totalPastDue: periodicals.totalPastDue)
}
}
.navigationTitle(title)
.toolbar
{
ToolbarItemGroup(placement: .navigationBarTrailing)
{
if periodicals.previousItem != nil
{
Button(action: { periodicals.undo() })
{ undoButtonImage }
}
NavigationLink { AddView() }
label: { addButtonImage }
if !periodicals.items.isEmpty
{
Button(action: { showingSearchBar.toggle() })
{ showingSearchBar ? searchButtonImage2 : searchButtonImage}
}
}
}
}
.navigationViewStyle(.stack)
.alert(item: $periodicals.appError)
{ appAlert in
Alert(title: Text(periodicals.errorHeading),
message: Text(appAlert.errorString))
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification))
{ _ in today = Date.now }
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification))
{ _ in periodicals.setNotifications() }
}
.onChange(of: filter). // Cannot convert value of type 'Filter' to expected argument type '()'
{
let generator = UISelectionFeedbackGenerator()
generator.selectionChanged()
}
}
// The search text is trimmed of leading and trailing spaces
// unless it consists of only spaces.
var filteredSearchResults: [Periodical]
{
var searchText = self.searchText
var items = periodicals.items
if filter == .bills
{ items = items.filter { $0.price != nil }}
else if filter == .tasks
{ items = items.filter { $0.price == nil }}
if !searchText.trimmed().isEmpty
{ searchText = searchText.trimmed() }
if searchText.isEmpty
{ return items }
else
{ return items.filter
{ $0.name.localizedCaseInsensitiveContains(searchText) }
}
}
private func removeItem(at offsets: IndexSet)
{
let id = filteredSearchResults[offsets.first!].id
periodicals.removeItem(id: id)
}
}
struct ItemsView_Previews: PreviewProvider {
static var previews: some View {
ItemsView()
.environmentObject(Periodicals())
}
}