Using KeyFrameTimeline as value for .keyFrameAnimator view modifier

I have several views I want to animate in concert. I plan to use keyframe animation. I want to keep the .keyFrameAnimator modifier code small; I have a lot of ...Keyframes inside several KeyframeTracks. It seems like I should be able to isolate the body of the keyframes parameter into a func or var. Builders are such a pain, I can't grok the right way to refactor their bodies out.

I've tried to make a standalone @KeyframeTrackContentBuilder<MyValue> function but cannot figure out the right syntax/incantation to stuff it with KeyframeTracks.

My latest attempt is to create a func that returns a KeyframeTimeline, but that's been a deadend too.

let k: KeyframeTimeline<MyValue> = timeline(...)
CartoonCardView(color: .yellow)
    .keyframeAnimator(
        initialValue: k.value(time: 0)
    ) { content, value in
        content
            .rotationEffect(value.angle)
            .scaleEffect(value.scale)
            .offset(value.offset)
    } keyframes: { _ in k }

The error on k in the last line is "No exact matches in call to static method 'buildExpression'" with the sub-error "Candidate requires that 'KeyframeTimeline<MyValue>' conform to 'KeyframeTrackContent' (requirement specified as 'K' : 'KeyframeTrackContent') (SwiftUICore.KeyframesBuilder)"

Here is a more complete example of what I'm trying for:

import SwiftUI


struct Mark {
    var offset: CGSize
    var angle: Angle
    init(offset: CGSize, angle: Angle) {
        self.offset = offset
        self.angle = angle
    }
    init(x: CGFloat, y: CGFloat, angle: Angle) {
        self.offset = CGSize(width: x, height: y)
        self.angle = angle
    }
}

struct ContentView: View {
    static let blackMarks: [Mark] = [
        Mark(x: 0, y: 0, angle: .degrees(0)),
        Mark(x: 50, y: 0, angle: .degrees(-90)),
        Mark(x: 50, y: -100, angle: .degrees(-180)),
        Mark(x: 0, y: -100, angle: .degrees(-270)),
        Mark(x: 0, y: 0, angle: .degrees(-360))
    ]
    static let redMarks: [Mark] = [
        Mark(x: 0, y: 0, angle: .degrees(0)),
        Mark(x: -50, y: 0, angle: .degrees(90)),
        Mark(x: -50, y: 100, angle: .degrees(180)),
        Mark(x: 0, y: 100, angle: .degrees(270)),
        Mark(x: 0, y: 0, angle: .degrees(360))
    ]

    var body: some View {
        ZStack {
            Rectangle().fill(.black).frame(width: 20, height: 30)
                .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in
                    content
                        .rotationEffect(value.angle)
                        .offset(value.offset)
                } keyframes: { _ in
                    KeyframeTrack(\.offset) {
                        LinearKeyframe(Self.blackMarks[1].offset, duration: 1.0)
                        LinearKeyframe(Self.blackMarks[2].offset, duration: 1.0)
                        LinearKeyframe(Self.blackMarks[3].offset, duration: 1.0)
                        LinearKeyframe(Self.blackMarks[4].offset, duration: 1.0)
                    }
                    KeyframeTrack(\.angle) {
                        LinearKeyframe(Self.blackMarks[1].angle, duration: 1.0)
                        LinearKeyframe(Self.blackMarks[2].angle, duration: 1.0)
                        LinearKeyframe(Self.blackMarks[3].angle, duration: 1.0)
                        LinearKeyframe(Self.blackMarks[4].angle, duration: 1.0)
                    }
                }
            Rectangle().fill(.red).frame(width: 20, height: 30)
                .keyframeAnimator(initialValue: Self.blackMarks[0]) { content, value in
                    content
                        .rotationEffect(value.angle)
                        .offset(value.offset)
                } keyframes: { _ in
                    /* Now, generate the equivalent tracks and keyframes as a function of redMarks */
                }

        }
    }
}

#Preview {
    ContentView()
}
Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
 
 
Q