Hi,
The goal is the move an element based on an input matrix using timer to trigger changes in the position. Say we have 2 positions, an input matrix and the element that is to be moved defined:
import SwiftUI import Foundation
// Positions
struct PositionConstants {
static let pos_1 = CGPoint(x: 155, y: 475)
static let pos_2 = CGPoint(x: 135, y: 375)
}
// Input Matrix
struct Input {
static let input_1: [[Int]] = [
[ 1, 1, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 1, 0, 1, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
]
}
// Element Class
class Element: ObservableObject {
let imageName: String
let name: String
let size: CGSize
@Published var position: CGPoint
init(imageName: String, name: String, size: CGSize, position: CGPoint) {
self.imageName = imageName
self.name = name
self.size = size
self.position = position
}
}
Moving the element without a function works perfectly fine:
struct Pos_View: View {
@ObservedObject var element_1 = Element(imageName: "pic_1", name: "", size: CGSize(width: 100, height: 100), position: PositionConstants.pos_1)
let Input_Matrix = Input.input_1
// Index to track current row in matrix
@State private var currentIndex = 0
// Timer to trigger updates
private let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
// Display element
Image(element_1.imageName)
.resizable()
.frame(width: element_1.size.width, height: element_1.size.height)
.position(element_1.position)
.onReceive(timer) { _ in
// Update element position based on matrix
if currentIndex < Input_Matrix.count {
let isFirstElementOne = Input_Matrix[currentIndex][0] == 1
let newPosition = isFirstElementOne ? PositionConstants.pos_2 : PositionConstants.pos_1
withAnimation {
element_1.position = newPosition
}
currentIndex += 1
} else {
// Stop the timer when we reach the end of the matrix
timer.upstream.connect().cancel()
}
}
}
}
}
struct ContentView: PreviewProvider {
static var previews: some View {
Pos_View()
}
}
But when using a function to trigger the animation, I get the error "Cannot use mutating member on immutable value: 'self' is immutable" when calling the function using a button:
struct Pos_View: View {
@ObservedObject var element_1 = Element(imageName: "pic_1", name: "", size: CGSize(width: 100, height: 100), position: PositionConstants.pos_1)
let Input_Matrix = Input.input_1
// Index to track current row in matrix
@State var currentIndex = 0
// Timer to trigger updates
private var timer: Timer?
var body: some View {
VStack {
// Display element
Image(element_1.imageName)
.resizable()
.frame(width: element_1.size.width, height: element_1.size.height)
.position(element_1.position)
// Button to start animation
Button("Start Animation") {
startAnimation()
}
}
}
mutating func startAnimation() {
currentIndex = 0 // Reset index before starting animation
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {timer in
if self.currentIndex < self.Input_Matrix.count {
let isFirstElementOne = self.Input_Matrix[self.currentIndex][0] == 1
let newPosition = isFirstElementOne ? PositionConstants.pos_2 : PositionConstants.pos_1
withAnimation {
self.element_1.position = newPosition
}
self.currentIndex += 1
} else {
// Stop the timer when we reach the end of matrix
timer.invalidate()
}
}
}
}
struct ContentView: PreviewProvider {
static var previews: some View {
Pos_View()
}
}
The function is defined as mutating and the element as as @ObservedObject. Anyone has a clue?