In SwiftUI how can I animate with an offset in both x and y without causing a compiler error

I am having trouble animating with an offset in both x and y at the same time and want to know how to change things so I get a successful build.

I can either build successfully as shown (with y animating correctly and x using 0) or I can swap the commenting around and have x animating correctly with y using 0 or 0.

If I change the commenting to allow both x and y to animate (neither using 0 as the first part of the expression), the compiler fails with the dreaded “The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions”.

I’ve tried refactoring in lots of different ways and the way I’ve set and used the constants a and i are the simplest I can manage (and I’ve tried many different ways!)

ZStack {
	let numSlots = kidsMaxSlots
	let gwidth = geometry.size.width
	let gameplayArea = geometry.size.height * gwidth
	let slotsArea = gameplayArea / CGFloat(numSlots)
	let slotSize = slotsArea.squareRoot()
	let widthInSlots = Int(geometry.size.width / slotSize + 0.5)
	let newSlotSize = geometry.size.width / CGFloat(widthInSlots)
	let a = Int(geometry.size.width / (geometry.size.width / CGFloat(Int(geometry.size.width / (geometry.size.height * geometry.size.width / CGFloat(numSlots)).squareRoot() + 0.5))))
	let i = (geometry.size.width / (geometry.size.height * geometry.size.width / CGFloat(numSlots)).squareRoot()).rounded()
	let myShift = newSlotSize / 2
	ForEach(gameplayDisplayArea.slotsArray.indices){ mySequence in
		gameplayDisplayArea.slotsArray[gameplayDisplayArea.slotsArray[mySequence].id].slotImage
			.resizable()
			.frame(width: newSlotSize, height: newSlotSize)
			.background(Color.green.opacity(0.4))
			.position(
				x: myShift + CGFloat(mySequence % a) * newSlotSize,
				y: myShift + CGFloat(mySequence / widthInSlots) * newSlotSize
			)
			.offset(x: self.myAnimDefs.startAnimation ?
//						geometry.size.width / 2 - myShift - CGFloat(mySequence % a) * newSlotSize
						0
						: 0,
					y: self.myAnimDefs.startAnimation ?
						geometry.size.height - CGFloat(floor(CGFloat(mySequence) / i)) * newSlotSize
//						0
						: 0)
	}
}

Ack!
I lost sight of what I was trying to achieve and got hung up on trying to achieve it that way. It would still be good to know how to get the compiler to build as per my original question.

I now realize that I can achive what I was after by doing it all with .position like this:

.position(x: self.myAnimDefs.startAnimation ?
            geometry.size.width / 2
            : myShift + CGFloat(mySequence % a) * newSlotSize,
          y: self.myAnimDefs.startAnimation ?
            geometry.size.height + myShift
            : myShift + CGFloat(mySequence / widthInSlots) * newSlotSize
)

Pull your calculations out into a function and use that instead of inlining all that logic. SwiftUI Views should be kept as logic-free as possible. If you see all those lets and math in a View, you need to refactor.

1 Like