DEV Community

Hector Moreno
Hector Moreno

Posted on

Simple loader with SwiftUI

as first step we make a struct view in this case we'll call it LoaderView

struct LoaderView: View {}
Enter fullscreen mode Exit fullscreen mode

Next, we will declare a boolean variable that will start the animation and make the colors array to animation.

struct LoaderView: View {
    let isAnimating: Bool
    private let color: [Color] = [.red, .orange, .yellow, .green, .blue]
}
Enter fullscreen mode Exit fullscreen mode

After declaring our variables, we will create a state variable to rotate our component.

struct LoaderView: View {
    let isAnimating: Bool
    private let color: [Color] = [.red, .orange, .yellow, .green, .blue]
    @State private var rotations: [Double] = Array(repeating: 0, count: 5)
}
Enter fullscreen mode Exit fullscreen mode

Use the body property required by the View protocol; within that property, add a horizontal stack, and inside the stack, use a ForEach to iterate over your design.

struct LoaderView: View {
    let isAnimating: Bool
    private let color: [Color] = [.red, .orange, .yellow, .green, .blue]
    @State private var rotations: [Double] = Array(repeating: 0, count: 5)

var body: some View {
        HStack {
            ForEach(0..<5, id: \.self) { i in
}}}
}
Enter fullscreen mode Exit fullscreen mode

Inside the forEach, we will use a Circle() and add a rotationEffect(.degrees()) to it; for the degrees, we will pass the rotations state corresponding to the circle's position.

struct LoaderView: View {
    let isAnimating: Bool
    private let colors: [Color] = [.red, .orange, .yellow, .green, .blue]
    @State private var rotations: [Double] = Array(repeating: 0, count: 5)

    var body: some View {
        HStack {
            ForEach(0..<5, id: \.self) { i in
                Spacer()
                Circle()
                    .strokeBorder(
                        style: StrokeStyle(lineWidth: 4, lineCap: .round, dash: [2,4,6]),
                        antialiased: true
                    )
                    .foregroundColor(colors[i])
                    .frame(width: 50, height: 50)
                    .rotationEffect(.degrees(rotations[i]))
            }
        }
Enter fullscreen mode Exit fullscreen mode

In the end, we will have the design shown in the following image:

To animate the rotation of each circle, we will use a task within the HStack that executes whenever the isAnimating variable changes; as long as the task is not cancelled, it will perform the animation over a period of 0.6 seconds, with a change occurring every 0.25 seconds.

In the end, our code will look like this:

struct LoaderView: View {
    let isAnimating: Bool
    private let colors: [Color] = [.red, .orange, .yellow, .green, .blue]
    @State private var rotations: [Double] = Array(repeating: 0, count: 5)

    var body: some View {
        HStack {
            ForEach(0..<5, id: \.self) { i in
                Spacer()
                Circle()
                    .strokeBorder(
                        style: StrokeStyle(lineWidth: 4, lineCap: .round, dash: [2,4,6]),
                        antialiased: true
                    )
                    .foregroundColor(colors[i])
                    .frame(width: 50, height: 50)
                    .rotationEffect(.degrees(rotations[i]))
            }
        }
        .task(id: isAnimating) {
            guard isAnimating else { return }
            while !Task.isCancelled {
                for i in 0..<colors.count {
                    withAnimation(.linear(duration: 0.6)) {
                        rotations[i] += 360
                    }
                    guard (try? await Task.sleep(nanoseconds: 250_000_000)) != nil else { return }
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, whenever you want to use your loader, you simply pass true or false; this gives you control over it whenever you call a service or just want to load the screen.

LoaderView(isAnimating: false)
LoaderView(isAnimating: true)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)