SwiftUI View updating
Solution 1:
You just have to observe the object at the appropriate level.
Each @Published
only triggers a refresh if the object as a whole has changed.
In you example the array will change if you replace the array or add/remove objects.
import SwiftUI
struct ExerciseProgramView: View {
//At this level you will see the entire program
@StateObject var program: ExerciseProgram = ExerciseProgram()
var body: some View {
VStack{
if program.currentExercise != nil{
ExerciseView(exercise: program.currentExercise!)
}else{
Text("Ready?")
}
Spacer()
HStack{
if program.currentExercise == nil{
Button("start program", action: {
program.start()
})
}else{
Button("stop", action: {
program.stop()
})
Button("next", action: {
program.next()
})
}
}
}
}
}
struct ExerciseView: View {
//At this level you will see the changes for the exercise
@ObservedObject var exercise: Exercise
var body: some View {
VStack{
Text("\(exercise.name)")
Text("\(exercise.currentSet)")
if exercise.timer == nil{
Button("start exercise", action: {
exercise.start()
})
}else{
Button("stop exercise", action: {
exercise.stop()
})
}
}.onDisappear(perform: {
exercise.stop()
})
}
}
struct ExerciseProgramView_Previews: PreviewProvider {
static var previews: some View {
ExerciseProgramView()
}
}
class Exercise: ObservableObject, Identifiable {
let id: UUID = UUID()
let name: String
@Published var currentSet: Int = 1
var timer : Timer?
init(name: String){
self.name = name
}
func start() {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
self.currentSet += 1
if self.currentSet >= 10{
timer.invalidate()
self.timer = nil
}
})
}
func stop(){
timer?.invalidate()
timer = nil
}
}
class ExerciseProgram: ObservableObject {
@Published var currentExercise: Exercise? = nil
@Published var exercises: [Exercise] = [Exercise(name: "neck"), Exercise(name: "arm"), Exercise(name: "leg"), Exercise(name: "abs")]
@Published var exerciseIndex: Int = 0
func start() {
self.currentExercise = self.exercises[exerciseIndex]
}
func next(){
if exerciseIndex < exercises.count{
self.exerciseIndex += 1
}else{
self.exerciseIndex = 0
}
start()
}
func stop(){
exerciseIndex = 0
currentExercise = nil
}
}
Also, notice how the ObservableObject
s have been initialized.
@StateObject
is used when the object has to be initialized in a View
@ObservedObject
is used to pass an ObservableObject
to a child View
but the object was created inside a class
, specifically class ExerciseProgram
.
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app