Hi, new forum member here, although I'm on Day 47 of the program. I've got the habit tracker working to a point. I can add a habit, see the detail view, and increase the number of times it is completed, but I don't see the count increase when I tap the button. I have to leave the detail view and then open it again to see the increased count. I assume it must have something to do with how I'm managing state, but I guess I don't fully understand all the concepts yet. Any help would be appreciated.
UPDATE: I used a LazyVGrid instead of this complicated custom grid layout and it just worked.
ContentView:
import SwiftUI
struct HabitItem: Identifiable, Codable, Equatable {
var id = UUID()
let name: String
let description: String
var completed: Int
}
@Observable
class Habits {
var items = [HabitItem]() {
didSet {
if let encoded = try? JSONEncoder().encode(items) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([HabitItem].self, from: savedItems) {
items = decodedItems
return
}
}
items = []
}
}
struct ContentView: View {
@State private var habits = Habits()
@State private var showingAddHabit = false
var body: some View {
NavigationStack {
List {
ForEach(habits.items) { item in
NavigationLink {
HabitView(habits: habits, habit: item)
} label: {
HStack {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
}
}
}
}
}
.navigationTitle("Habit Tracker")
Button("Add a Habit to Track") {
showingAddHabit = true
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
}
.sheet(isPresented: $showingAddHabit) {
AddHabitView(habits: habits)
}
}
}
#Preview {
ContentView()
}
And HabitView
import SwiftUI
struct HabitView: View {
var habits = Habits()
let habit: HabitItem
@State var numCells: (Int, Int)
init(habits: Habits = Habits(), habit: HabitItem) {
self.habits = habits
self.habit = habit
_numCells = State(initialValue: habit.completed.quotientAndRemainder(dividingBy: 20))
}
var body: some View {
Text(habit.name)
.font(.largeTitle)
Text(habit.description)
ScrollView {
Grid {
ForEach(0..<numCells.0 + 1, id: \.self) {number in
GridRow {
if number + 1 == numCells.0 + 1 {
ForEach(0..<numCells.1) {cell in
Rectangle()
.frame(width: 10, height: 10)
.foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
}
} else {
ForEach(0..<20) {cell in
Rectangle()
.frame(width: 10, height: 10)
.foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
}
}
}
}
}
}
Button("Did It") {
var tempHabit = habit
tempHabit.completed += 1
if let index = habits.items.firstIndex(of: habit) {
habits.items[index] = tempHabit // index is not optional any more
}
}
.buttonStyle(.borderedProminent)
}
}
#Preview {
HabitView(habit: HabitItem(id: UUID(), name: "Name", description: "Foo", completed: 257))
}