SwiftUI - How to add "letters sections" and alphabet jumper in a Form? - menu

How can I make a Form in which the elements are automatically divided into sections based on their first letter and add to the right the alphabet jumper to show the elements starting by the selected letter (just like the Contacts app)?
I also noted a strange thing that I have no idea how to recreate: not all letters are shown, some of them appear as "•". However, when you tap on them, they take you to the corresponding letter anyway. I tried using a ScrollView(.vertical) inside a ZStack and adding .scrollTo(selection) into the action of the Buttons, however 1) It didn't scroll to the selection I wanted 2) When I tapped on the "•", it was as if I was tapping on all of them because they all did the tapping animation 3) I wasn't able to divide the List as I wanted to.
I have this:
import SwiftUI
struct ContentView: View {
let alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W", "X","Y", "Z"]
let values = ["Avalue", "Bvalue", "Cvalue", "Dvalue"]
var body: some View {
ScrollViewReader{ scrollviewr in
ZStack {
ScrollView(.vertical) {
VStack {
ForEach(alphabet, id: \.self) { letters in
Button(letters){
withAnimation {
scrollviewr.scrollTo(letters)
}
}
}
}
}.offset(x: 180, y: 120)
VStack {
ForEach(values, id: \.self){ vals in
Text(vals).id(vals)
}
}
}
}
}
}
But I'd want it like this:

import SwiftUI
struct AlphabetSort2: View {
let alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W", "X","Y", "Z"]
let values = ["Avalue", "Bvalue", "Cvalue", "Dvalue", "Mvalue", "Zvalue"]
var body: some View {
ScrollView {
ScrollViewReader { value in
ZStack{
List{
ForEach(alphabet, id: \.self) { letter in
Section(header: Text(letter)) {
ForEach(values.filter { $0.hasPrefix(letter) }, id: \.self) { vals in
Text(vals).id(vals)
}
}.id(letter)
}
}
HStack{
Spacer()
VStack {
ForEach(0..<alphabet.count, id: \.self) { idx in
Button(action: {
withAnimation {
value.scrollTo(alphabet[idx])
}
}, label: {
Text(idx % 2 == 0 ? alphabet[idx] : "\u{2022}")
})
}
}
}
}
}
}
}
}
struct AlphabetSort2_Previews: PreviewProvider {
static var previews: some View {
AlphabetSort2()
}
}

Add an swipe-up-and-down action for the alphabet jumper, so we can get an UILocalizedIndexCollation -like swipe effect, it works for view and add mode, but delete, I guess it is due to SwiftUI's UI refresh mechanism.
extension String {
static var alphabeta: [String] {
var chars = [String]()
for char in "abcdefghijklmnopqrstuvwxyz#".uppercased() {
chars.append(String(char))
}
return chars
}
}
struct Document: View {
#State var items = ["Alpha", "Ash", "Aman", "Alisia", "Beta", "Baum", "Bob", "Bike", "Beeber", "Beff", "Calipha", "Cask", "Calf", "Deamon", "Deaf", "Dog", "Silk", "Seal", "Tiger", "Tom", "Tan", "Tint", "Urshinabi", "Verizon", "Viber", "Vein", "Wallet", "Warren", "Webber", "Waiter", "Xeon", "Young", "Yoda", "Yoga", "Yoger", "Yellow", "Zeta"]
var body: some View {
ScrollViewReader { scrollView in
HStack {
List {
ForEach(String.alphabeta, id: \.self){ alpha in
let subItems = items.filter({$0.starts(with: alpha)})
if !subItems.isEmpty {
Section(header: Text(alpha)) {
ForEach(subItems, id: \.self) { item in
Text(item)
}.onDelete(perform: { offsets in
items.remove(at: offsets.first!)
})
}.id(alpha)
}
}
}
VStack{
VStack {
SectionIndexTitles(proxy: scrollView, titles: retrieveSectionTitles()).font(.footnote)
}
.padding(.trailing, 10)
}
}
}
// .navigationBarTitleDisplayMode(.inline)
// .navigationBarHidden(true)
}
func retrieveSectionTitles() ->[String] {
var titles = [String]()
titles.append("#")
for item in self.items {
if !item.starts(with: titles.last!){
titles.append(String(item.first!))
}
}
titles.remove(at: 0)
if titles.count>1 && titles.first! == "#" {
titles.append("#")
titles.removeFirst(1)
}
return titles
}
}
struct Document_Previews: PreviewProvider {
static var previews: some View {
Document()
}
}
struct SectionIndexTitles: View {
class IndexTitleState: ObservableObject {
var currentTitleIndex = 0
var titleSize: CGSize = .zero
}
let proxy: ScrollViewProxy
let titles: [String]
#GestureState private var dragLocation: CGPoint = .zero
#StateObject var indexState = IndexTitleState()
var body: some View {
VStack {
ForEach(titles, id: \.self) { title in
Text(title)
.foregroundColor(.blue)
.modifier(SizeModifier())
.onPreferenceChange(SizePreferenceKey.self) {
self.indexState.titleSize = $0
}
.onTapGesture {
proxy.scrollTo(title, anchor: .top)
}
}
}
.gesture(
DragGesture(minimumDistance: indexState.titleSize.height, coordinateSpace: .named(titles.first))
.updating($dragLocation) { value, state, _ in
state = value.location
scrollTo(location: state)
}
)
}
private func scrollTo(location: CGPoint){
if self.indexState.titleSize.height > 0{
let index = Int(location.y / self.indexState.titleSize.height)
if index >= 0 && index < titles.count {
if indexState.currentTitleIndex != index {
indexState.currentTitleIndex = index
print(titles[index])
DispatchQueue.main.async {
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
// withAnimation {
proxy.scrollTo(titles[indexState.currentTitleIndex], anchor: .top)
// }
}
}
}
}
}
}
struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct SizeModifier: ViewModifier {
private var sizeView: some View {
GeometryReader { geometry in
Color.clear.preference(key: SizePreferenceKey.self, value: geometry.size)
}
}
func body(content: Content) -> some View {
content.background(sizeView)
}
}

Related

Refresh a view when the children of an object are changed in SwiftUI

I am working on a CoreData application with two entities MyList and MyListItem. MyList can have many MyListItem (one to many). When the app is launched, I can see all the lists. I can tap on a list to go to the list items. On that screen, I tap a button to add an item to the selected list. After, adding the item when I go back to the all lists screen I cannot see the number of items reflected in the count. The reason is that MyListsView is not rendered again since the number of lists have not changed.
The complete code is shown below:
import SwiftUI
import CoreData
extension MyList {
static var all: NSFetchRequest<MyList> {
let request = MyList.fetchRequest()
request.sortDescriptors = []
return request
}
}
struct DetailView: View {
#Environment(\.managedObjectContext) var viewContext
let myList: MyList
var body: some View {
VStack {
Text("Detail View")
Button("Add List Item") {
let myListP = viewContext.object(with: myList.objectID) as! MyList
let myListItem = MyListItem(context: viewContext)
myListItem.name = randomString()
myListItem.myList = myListP
try? viewContext.save()
}
}
}
func randomString(length: Int = 8) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
}
class ViewModel: NSObject, ObservableObject {
#Published var myLists: [MyList] = []
private var fetchedResultsController: NSFetchedResultsController<MyList>
private(set) var context: NSManagedObjectContext
override init() {
self.context = CoreDataManager.shared.context
fetchedResultsController = NSFetchedResultsController(fetchRequest: MyList.all, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
super.init()
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
guard let myLists = fetchedResultsController.fetchedObjects else { return }
self.myLists = myLists
} catch {
print(error)
}
}
}
extension ViewModel: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard let myLists = controller.fetchedObjects as? [MyList] else { return }
self.myLists = myLists
}
}
struct MyListsView: View {
let myLists: [MyList]
var body: some View {
List(myLists) { myList in
NavigationLink {
DetailView(myList: myList)
} label: {
HStack {
Text(myList.name ?? "")
Spacer()
Text("\((myList.items ?? []).count)")
}
}
}
}
}
struct ContentView: View {
#StateObject private var vm = ViewModel()
#Environment(\.managedObjectContext) var viewContext
var body: some View {
NavigationView {
VStack {
// when adding an item to the list the MyListView view is
// not re-rendered
MyListsView(myLists: vm.myLists)
Button("Change List") {
}
}
}
}
func randomString(length: Int = 8) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
}
Inside ContentView there is a view called "MyListsView". That view is not rendered when the items are added. Since, according to that view nothing changed since the number of lists are still the same.
How do you solve this problem?
UPDATE:
What happens if I add one more level of views like for ListCellView as shown below:
struct MyListCellView: View {
#StateObject var vm: ListCellViewModel
init(vm: ListCellViewModel) {
_vm = StateObject(wrappedValue: vm)
}
var body: some View {
HStack {
Text(vm.name)
Spacer()
Text("\((vm.items).count)")
}
}
}
#MainActor
class ListCellViewModel: ObservableObject {
let myList: MyList
init(myList: MyList) {
self.myList = myList
self.name = myList.name ?? ""
self.items = myList.items!.allObjects as! [MyListItem]
print(self.items.count)
}
#Published var name: String = ""
#Published var items: [MyListItem] = []
}
struct MyListsView: View {
#StateObject var vm: ViewModel
init(vm: ViewModel) {
_vm = StateObject(wrappedValue: vm)
}
var body: some View {
let _ = Self._printChanges()
List(vm.myLists) { myList in
NavigationLink {
DetailView(myList: myList)
} label: {
MyListCellView(vm: ListCellViewModel(myList: myList))
}
}
}
}
Now the count is again not being updated.
Your ViewModel is an ObserveableObject, but you are not observing it in MyListsView. When you initialized MyListsView, you set a let constant. Of course that won't update. Do this instead:
struct MyListsView: View {
#ObservedObject var vm: ViewModel
init(viewModel: ViewModel) {
self.vm = viewModel
}
var body: some View {
List(vm.myLists) { myList in
NavigationLink {
DetailView(myList: myList)
} label: {
HStack {
Text(myList.name ?? "")
Spacer()
Text("\((myList.items ?? []).count)")
}
}
}
}
}
Now the #Published in ViewModel will cause MyListView to change when it does, and that includes adding a related entity.
We don't need MVVM in SwiftUI, the View data structs already fill that role and property wrappers make them behave like objects giving best of both worlds. In your case use the #FetchRequest property wrapper for the list and #ObservedObject for the detail and body will be called on any changes to the model data. Examine the code in the app template in Xcode with Core Data checked. It looks like this:
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink {
DetailView(item: item)
} label: {
Text(item.timestamp!, formatter: itemFormatter)
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
Text("Select an item")
}
}
...
struct DetailView: View {
#ObservedObject var item: Item
var body: some View {
Text("Item at \(item.timestamp!, formatter: itemFormatter)")

Core data not triggering immediate refresh with SwiftUI

I'm at a loss with this problem I've been troubleshooting the past few days.
I am drawing a custom shape, and the user can move one of the points around via a drag gesture. I want the shape to redraw, live, when moving the point around.
Here is some sample code that is working using CGPoint.
import SwiftUI
struct ShapeTest: Shape {
#State var points: [CGPoint]
#State var closed: Bool = true
func path(in rect: CGRect) -> Path {
var path = Path()
if (points.count > 0) {
path.move(to: points.first!)
path.addLines(points)
if closed { path.closeSubpath() }
}
return path
}
}
struct RedrawEdgeTestCGPoint: View {
#State var points = [CGPoint]()
#State var originalPosition: CGPoint? = nil
private var movePointDragGesture: some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged { value in
if points.isEmpty {
return
}
if originalPosition == nil {
originalPosition = CGPoint(x: points.last!.x, y: points.last!.y)
}
let lastIndex = points.count - 1
points[lastIndex].x = originalPosition!.x + value.translation.width
points[lastIndex].y = originalPosition!.y + value.translation.height
}
.onEnded { value in
originalPosition = nil
}
}
private var addNewPointGesture: some Gesture {
TapGesture(count: 2)
.onEnded {
points.append(CGPoint())
}
}
var body: some View {
GeometryReader { geometry in
Group {
ShapeTest(points: points)
.stroke()
}
.contentShape(Rectangle())
.gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
}
}
}
struct RedrawEdgeTestCGPoint_Previews: PreviewProvider {
static var previews: some View {
RedrawEdgeTestCGPoint()
}
}
Now, here is another code sample. This example is generally the same thing, but this is using a Point entity I've defined in Core Data instead of CGPoint.
import SwiftUI
struct RedrawEdgeTestCoreData: View {
#Environment(\.managedObjectContext) private var viewContext
#State var points: [Point] = []
#State var originalPosition: CGPoint? = nil
private var movePointDragGesture: some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged { value in
if points.isEmpty {
return
}
if originalPosition == nil {
originalPosition = CGPoint(x: points.last!.x, y: points.last!.y)
}
let lastIndex = points.count - 1
points[lastIndex].x = originalPosition!.x + value.translation.width
points[lastIndex].y = originalPosition!.y + value.translation.height
}
.onEnded { value in
originalPosition = nil
}
}
private var addNewPointGesture: some Gesture {
TapGesture(count: 2)
.onEnded {
points.append(Point(context: viewContext))
}
}
var body: some View {
GeometryReader { geometry in
Group {
ShapeTest(points: points.map { CGPoint(x: $0.x, y: $0.y) })
.stroke()
}
.contentShape(Rectangle())
.gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
}
}
}
struct RedrawEdgeTestCoreData_Previews: PreviewProvider {
static var previews: some View {
RedrawEdgeTestCoreData()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
The version utilizing Core Data does not update while dragging. It will redraw when I double tap to add another point.
Any suggestions as to why this is happening?
I used the approach suggested by jnpdx and it works.
import SwiftUI
class PointsViewModel: ObservableObject {
#Published var points = [Point]()
func movePoint(index: Int, x: CGFloat, y: CGFloat) {
points[index].x = x
points[index].y = y
objectWillChange.send()
}
}
struct RedrawEdgeTestCoreData: View {
#Environment(\.managedObjectContext) private var viewContext
#ObservedObject var viewModel: PointsViewModel
#State var originalPosition: CGPoint? = nil
private var movePointDragGesture: some Gesture {
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged { value in
if viewModel.points.isEmpty {
return
}
if originalPosition == nil {
originalPosition = CGPoint(x: viewModel.points.last!.x, y: viewModel.points.last!.y)
}
let lastIndex = viewModel.points.count - 1
viewModel.movePoint(index: lastIndex, x: originalPosition!.x + value.translation.width, y: originalPosition!.y + value.translation.height)
}
.onEnded { value in
originalPosition = nil
}
}
private var addNewPointGesture: some Gesture {
TapGesture(count: 2)
.onEnded {
viewModel.points.append(Point(context: viewContext))
}
}
var body: some View {
GeometryReader { geometry in
ZStack {
ShapeTest(points: viewModel.points.map { CGPoint(x: $0.x, y: $0.y) })
.stroke()
}
.contentShape(Rectangle())
.gesture(movePointDragGesture.simultaneously(with: addNewPointGesture))
}
}
}
struct RedrawEdgeTestCoreData_Previews: PreviewProvider {
static var previews: some View {
RedrawEdgeTestCoreData(viewModel: PointsViewModel())
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

How to show a value from CoreData Entity as a state variable after editing

I'm currently developing an application using CoreData in SwiftUI.
I want to show a value from CoreData Entity as a state variable after I edit that data.
When I add a value as a new date I can show that value well, but after I edit the value it doesn't reflect the editing...
If I don't use #State, the value can be shown well, even after I edit it. But I need to use as #State value to bind it to a child view.
How could I solve this problem?
(I want to show the collect value after editing in a place I comment out as B same as like A in ListView.swift )
CDTest.xcdatamodeld
CDTestApp.swift
import SwiftUI
#main
struct CDTestApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
ListView()
}
}
PersistenceController.swift
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "CDTest")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
HomeViewModel.swift
import SwiftUI
import CoreData
class HomeViewModel :ObservableObject{
#Published var content = ""
//for newdata sheet
#Published var isNewDate = false
//storing update Item
#Published var updateItem : Task!
func writeData(context : NSManagedObjectContext){
if updateItem != nil {
updateItem.content = content
try! context.save()
updateItem = nil
isNewDate.toggle()
content = ""
return
}
let newTask = Task(context: context)
newTask.content = content
do{
try context.save()
isNewDate.toggle()
content = ""
}catch{
print(error.localizedDescription)
}
}
func EditItem(item:Task){
updateItem = item
content = item.content!
isNewDate.toggle()
}
}
ListView.swift
import SwiftUI
import CoreData
struct ListView: View {
#StateObject var homeData = HomeViewModel()
#FetchRequest(entity: Task.entity(), sortDescriptors: [NSSortDescriptor(key:"date",
ascending:true)],animation:.spring()) var results:FetchedResults<Task>
#Environment(\.managedObjectContext) var context
var body: some View {
NavigationView{
VStack(spacing:0){
//Empty View...
if results.isEmpty{
Text("NO Data")
}else{
LazyVStack{
ForEach(results){task in
VStack(alignment: .leading, spacing: 5, content: {
Text(task.content ?? "") // A
ListRow(content: task.content ?? "") // B
})
.contextMenu{
Button(action: {
homeData.EditItem(item: task)
}, label: {
Text("Edit")
})
Button(action: {
context.delete(task)
try! context.save()
}, label: {
Text("Delete")
})
}
}
}
.padding()
}
//Add Button...
Button(action: {homeData.isNewDate.toggle()}, label: {
Image(systemName: "plus")
.font(.largeTitle)
.padding()
})
.sheet(isPresented: $homeData.isNewDate, content: {
NewDataView(homeData: homeData)
})
}
}
}
}
ListRow.swift
import SwiftUI
struct ListRow: View {
#State var content:String
var body: some View {
Text(content)
NavigationLink(destination:DetailView(content: $content)){
Text("DETAIL")
}
}
}
DetailView.swift
import SwiftUI
struct DetailView: View {
#Binding var content:String
var body: some View {
Text(content)
}
}
NewDataView.swift
import SwiftUI
struct NewDataView: View {
#ObservedObject var homeData : HomeViewModel
#Environment(\.managedObjectContext) var context
var body: some View {
VStack{
Text("\(homeData.updateItem == nil ? "Add New" : "Up date")Task")
.font(.title)
TextEditor(text: $homeData.content)
.padding()
//Add Button...
Button(action: {homeData.writeData(context: context)}, label: {
Text(homeData.updateItem == nil ? "Add Now" : "Update")
.font(.title)
.fontWeight(.bold)
})
.padding()
.disabled(homeData.content == "" ? true : false)
.opacity(homeData.content == "" ? 0.5 : 1)
}
}
}
Xcode: Version 12.0.1
iOS: 14.0
Life Cycle: SwiftUI App
Just pass entire task object into ListRow, like
VStack(alignment: .leading, spacing: 5, content: {
Text(task.content ?? "") // A
ListRow(task: task) // B // << here !!
so now we can use ObservedObject wrapper, which gives binding to published property:
struct ListRow: View {
#ObservedObject var task: Task
var body: some View {
Text(task.content ?? "")
NavigationLink(destination:DetailView(content: $task.content)){
Text("DETAIL")
}
}
}

Swiftui updating menu component lists dynamically

I have two menu components and need to update the number of elements in the second menu once a selection is made in the fist menu component. Here is a simplified sample code:
import SwiftUI
struct ContentView: View {
#State var menu1List: [Int]
#State var menu2List: [String]
#State var currentIndex: Int
init() {
_menu1List = State(initialValue: [1, 2, 3, 4, 5, 6 , 7, 8])
_menu2List = State(initialValue: ["A", "B", "C", "D", "E", "F", "G", "H"])
_currentIndex = State(initialValue: 0)
}
var body: some View {
VStack(alignment: .leading) {
MenuView(menu1List: $menu1List, menu2List: $menu2List, currentIndex: $currentIndex)
ForEach(menu2List.indices) { index in
Text("Item \(menu2List[index])") \\Menu 1 selection in MenuView causes index out of range exception.
}
Spacer()
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct MenuView : View {
#Binding var menu1List: [Int]
#Binding var menu2List: [String]
#Binding var currentIndex: Int
var body: some View {
HStack {
Menu {
ForEach(menu1List.indices) { index in
Button(action: {
menu2List = ["A", "B", "C", "D"]
}
) {
Text("\(menu1List[index])")
}
}
} label: {
Text("Menu1")
Image(systemName: "chevron.down.circle")
}
Spacer()
Menu {
ForEach(menu2List.indices) { index in
Button(action: {
currentIndex = index
}
) {
Text("\(menu2List[index])")
}
}
} label: {
Text("Menu2")
Image(systemName: "chevron.down.circle")
}
}
.padding()
}
}
When I lower the number of elements in Menu 2 I get an Index out of range exception as indicated in the code. Is there any way to fix this? I would greatly appreciate any help on this.
You used static variant of ForEach constructor (ie it expected that data is not changed), but if you want to change content of ForEach you must use dynamic one (with id:)
ForEach(menu2List.indices, id: \.self) { index in // << here !!
Text("Item \(menu2List[index])")
}

Swift UI + CoreData + Toggle

I am using SwiftUI with CoreData. Each cell has a Toggle which updates CoreData isComplete: Bool. It works great if I have more than one item in core data, if I only have one item, I have to click the Toggle 3 times before it disappears or the view refreshes.
CategoryCompleteToggle(passedCategory: passedCategory, isOn: $isOn)
} // End HStack
Text(catName)
} // End VStack
.padding(10)
}
}
struct CategoryCompleteToggle: View {
var passedCategory: Category
#Environment(\.managedObjectContext) var moc
#ObservedObject var catToggle = CategoryToggle()
#Binding var isOn: Bool
var body: some View {
VStack(alignment: .trailing) {
Toggle(isOn: $isOn) {
EmptyView()
}
.onAppear {
self.isOn = self.passedCategory.catCompleted
print("ONLOAD ISON VALUE: \(self.isOn)")
}
.onTapGesture {
self.catToggle.catToggleOn.toggle()
self.catToggle.catToggleOn = !self.isOn
print("IsOn Value: \(self.isOn)")
print("Passed Category Name: \(self.passedCategory.catName ?? "Unknown")")
print("Current Completed Value: \(self.passedCategory.catCompleted)")
self.passedCategory.catCompleted = !self.isOn
do {
try self.moc.save()
} catch {
print(error.localizedDescription)
}

Resources