Lucas van Dongen
Lucas van Dongen

Reputation: 9848

How to make the LinePlot feature of Swift Charts dynamic and animated?

I'm trying to get the LinePlot feature from Xcode 16 to work. I tried the example code and it works, but I'm struggling to make it work interactively and animated.

Upvotes: 0

Views: 77

Answers (1)

Lucas van Dongen
Lucas van Dongen

Reputation: 9848

The answer has been taken from this blogpost about creating a spiral with LinePlot and animating it:

https://lucasvandongen.dev/recreating_ovo_timer_in_swiftui.php

The result looks something like this:

An example of a spiral drawn with LinePlot and two sliders to move the begin- and endpoints


import Charts
import SwiftUI

struct PlotPoint: Identifiable {
    let id: Int
    let x, y: Double
}

public struct Counter: View {
    private static let minimumAngle: CGFloat = 60
    private static let maximumAngle: CGFloat = 75

    @State private var beginAngle: CGFloat = Self.minimumAngle
    @State private var endAngle: CGFloat = Self.maximumAngle
    @State private var points: [PlotPoint] = [
        PlotPoint(
            id: 1,
            x: 0,
            y: 0
        ),
        PlotPoint(
            id: 2,
            x: 1,
            y: 3
        )
    ]

    public init() { }

    public var body: some View {
        Chart(points) {
            LinePlot(
                x: "x",
                y: "y",
                t: "t",
                domain: beginAngle...endAngle
            ) { t in
                spiral(t: t)
            }

            PointMark(
                x: .value("Wing Length", $0.x),
                y: .value("Wing Width", $0.y)
            ).symbol {
                Circle()
                    .fill(.white)
                    .stroke(
                        .blue,
                        lineWidth: 2
                    )
                    .frame(
                        width: 8,
                        height: 8
                    )
            }
        }
        .chartXScale(domain: -8...8)
        .chartYScale(domain: -8...8)
        .chartXAxis(.hidden)
        .chartYAxis(.hidden)
        .aspectRatio(
            1,
            contentMode: .fit
        )

        Slider(
            value: $beginAngle,
            in: Self.minimumAngle...endAngle
        )
        .onChange(
            of: beginAngle,
            perform: updateBeginPoint(to:)
        )
        .onAppear {
            updateBeginPoint(to: beginAngle)
        }

        Slider(
            value: $endAngle,
            in: beginAngle...Self.maximumAngle
        )
        .onChange(
            of: endAngle,
            perform: updateEndPoint(to:)
        )
        .onAppear {
            updateEndPoint(to: endAngle)
        }
    }

    private func updateBeginPoint(to newValue: Double) {
        let endPoint = spiral(t: newValue)

        points[0] = PlotPoint(
            id: 2,
            x: endPoint.0,
            y: endPoint.1
        )
    }

    private func updateEndPoint(to newValue: Double) {
        let endPoint = spiral(t: newValue)

        points[1] = PlotPoint(
            id: 2,
            x: endPoint.0,
            y: endPoint.1
        )
    }

    private func spiral(t: Double) -> (Double, Double) {
        let a: CGFloat = 0.1
        let b: CGFloat = 0.1
        let r = a + b * t
        let x = r * cos(t)
        let y = r * sin(t)

        return (x, y)
    }
}

Upvotes: 0

Related Questions