rotationEffect turns a view around a point. It takes an Angle and an optional anchor, and it is a drawing transform — the layout system still sees the view's original frame.

The shape of itthree forms
Rectangle()
    .rotationEffect(.degrees(45))                 // around the centre
    .rotationEffect(.degrees(45), anchor: .topLeading)
    .rotationEffect(.radians(.pi / 4))            // radians work too

Spinning forever

A continuous spin is one state flip plus a repeating linear animation.

SquareSpinner.swiftplays as written
import SwiftUI

struct SquareSpinner: View {
    @State private var turning = false

    var body: some View {
        ZStack {
            ForEach(0..<3, id: \.self) { i in
                RoundedRectangle(cornerRadius: 9)
                    .stroke(.orange, lineWidth: 3)
                    .frame(width: 44, height: 44)
                    .rotationEffect(.degrees(turning ? 180 : 0))
                    .scaleEffect(turning ? 0.55 : 1)
                    .opacity(1 - Double(i) * 0.3)
                    .animation(
                        .easeInOut(duration: 2.2)
                            .repeatForever()
                            .delay(Double(i) * 0.73),
                        value: turning
                    )
            }
        }
        .onAppear { turning = true }
    }
}

Two mistakes

1. autoreverses defaults to true

.repeatForever() winds back. For a spinner that means it turns clockwise, then unwinds anticlockwise, forever — which reads as broken rather than as loading. For continuous rotation you want:

Continuousnote the false
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: turning)

2. Order changes the picture

Modifiers wrap, so they apply outward-in, and rotation composes with position:

Order mattersnot the same
// Pushed out to a radius, then swung around the centre.
Capsule().offset(y: -17).rotationEffect(.degrees(30))

// Rotated in place, then pushed out. Twelve of these stack on top of each other.
Capsule().rotationEffect(.degrees(30)).offset(y: -17)

The first is how a twelve-dash spinner is built. The second is the bug report that says "all my dashes are in one spot".

Anchors

The anchor is a UnitPoint in the view's own space: .center by default, but .topLeading, .bottom and friends all work. A clock hand rotates around .bottom, not its centre — that one substitution is the difference between a hand that sweeps and a hand that tumbles.

Rotation that follows the clock

If the angle should be a function of real time rather than a transition between two values, compute it in a TimelineView instead of animating a state:

Time-drivenno @State at all
TimelineView(.animation) { context in
    let t = context.date.timeIntervalSinceReferenceDate
    Rectangle()
        .frame(width: 4, height: 22)
        .rotationEffect(.degrees(t * 90), anchor: .bottom)
}

No state, no onAppear, and it can never drift out of sync — the angle is derived from the clock every frame.

Run it

Paste SquareSpinner into Swoop and swap the offset/rotationEffect order, or move the anchor to .topLeading. Both mistakes above are much easier to remember once you have watched them happen.