Johnykutty
Johnykutty

Reputation: 12849

Activity indicator in SwiftUI

Trying to add a full screen activity indicator in SwiftUI.

I can use .overlay(overlay: ) function in View Protocol.

With this, I can make any view overlay, but I can't find the iOS default style UIActivityIndicatorView equivalent in SwiftUI.

How can I make a default style spinner with SwiftUI?

NOTE: This is not about adding activity indicator in UIKit framework.

Upvotes: 283

Views: 219216

Answers (17)

Swornim Shah
Swornim Shah

Reputation: 69

extract progress loader logic using modifiers. Simply call the addProgressAndLoader(...) from your Views. It's good to create a BaseViewModel and store @Published showAlert for making it common. You can simply pass your Binding as well instead of ViewModel

        import SwiftUI
    
        public extension View {
        
        @ViewBuilder
        func addProgressAndLoader(viewModel: ViewModel) -> some View {
            let alertBinding = Binding<Bool>(
                get: { viewModel.showAlert == true },
                        set: { _ in viewModel.showAlert = false }
                    )
            
            self.modifier(ProgressViewModifier(isLoading: viewModel.isLoading))
        }
    }
        
  struct ProgressViewModifier: ViewModifier {
            @EnvironmentObject var theme: ThemeManager
        
            var isLoading: Bool
            let isEnabled: Bool
            
            init(isLoading: Bool, isEnabled: Bool = true) {
                self.isLoading = isLoading
                self.isEnabled = isEnabled
            }
            
            func body(content: Content) -> some View {
                ZStack(alignment: .center) {
                    content
                        .disabled(isLoading)
                    if isLoading && isEnabled {
                        ProgressView()
                            .progressViewStyle(CircularProgressViewStyle(tint: theme.currentTheme.onSurfaceColor))
                    }
                }
            }
        }

    enter code here

Upvotes: 0

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119917

Using symbolEffect

You can create many effects with a little effort like these: Demo

For example, here is the code to recreate the default iOS progress indicator using symbols:

Image(systemName: "progress.indicator")
    .symbolEffect(
        .variableColor.iterative,
        options: .repeat(.continuous),
        value: isLoading
)

Note some of the effect and symbols are only available from specific platform version

Upvotes: 5

Muhammad Uzair
Muhammad Uzair

Reputation: 71

Activity Indicator or Loader

enter image description here

struct LoaderView: View {
    
 @State var isAnimating = false
    
    var body: some View {
        ZStack {
            Circle()
                .trim(from: 0, to: 0.7)
                .stroke(Color("Blue"), lineWidth: 4)
                .frame(width: 80, height: 80)
                .rotationEffect(.degrees(isAnimating ? 360 : 0))
                .foregroundColor(Color.blue)
                .onAppear() {
                    withAnimation(Animation
                                    .linear(duration: 1)
                                    .repeatForever(autoreverses: false)) {
                        self.isAnimating = true
                    }
                }
        }
    }
}

Usage:

struct HomeView: View {

  @State var isShowLoader : Bool = false
  
    var body: some View {
        ZStack {
           Button(action: {
                  isShowLoader.toggle()
                   }) {
                   Text("Loader")
                   }
                   .buttonStyle(.plain)
           if isShowLoader {
             LoaderView()
        }
     }
  }
} 

SwiftUI Activity indicator for macOS and iOS.

Upvotes: 6

KitKit
KitKit

Reputation: 9573

If you want to a swift-ui-style solution, then this is the magic:

import Foundation
import SwiftUI

struct ActivityIndicator: View {
    
    @State private var isAnimating: Bool = false
    
    var body: some View {
        GeometryReader { (geometry: GeometryProxy) in
            ForEach(0..<5) { index in
                Group {
                    Circle()
                        .frame(width: geometry.size.width / 5, height: geometry.size.height / 5)
                        .scaleEffect(calcScale(index: index))
                        .offset(y: calcYOffset(geometry))
                }.frame(width: geometry.size.width, height: geometry.size.height)
                    .rotationEffect(!self.isAnimating ? .degrees(0) : .degrees(360))
                    .animation(Animation
                                .timingCurve(0.5, 0.15 + Double(index) / 5, 0.25, 1, duration: 1.5)
                                .repeatForever(autoreverses: false))
            }
        }
        .aspectRatio(1, contentMode: .fit)
        .onAppear {
            self.isAnimating = true
        }
    }
    
    func calcScale(index: Int) -> CGFloat {
        return (!isAnimating ? 1 - CGFloat(Float(index)) / 5 : 0.2 + CGFloat(index) / 5)
    }
    
    func calcYOffset(_ geometry: GeometryProxy) -> CGFloat {
        return geometry.size.width / 10 - geometry.size.height / 2
    }
    
}

Simply to use:

ActivityIndicator()
.frame(width: 50, height: 50)

Hope it helps!

Example Usage:

ActivityIndicator()
    .frame(width: 200, height: 200)
    .foregroundColor(.orange)

enter image description here

Upvotes: 109

Brad
Brad

Reputation: 21

I have modified Matteo Pacini's Answer for macOS using AppKit and SwiftUI. This allows you to use NSProgressIndicator in SwiftUI while retaining capability for macOS 10.15.

import AppKit
import SwiftUI

struct ActivityIndicator: NSViewRepresentable {
    
    @Binding var isAnimating: Bool
    let style: NSProgressIndicator.Style

    func makeNSView(context: NSViewRepresentableContext<ActivityIndicator>) -> NSProgressIndicator {
        let progressIndicator = NSProgressIndicator()
        progressIndicator.style = self.style
        return progressIndicator
    }

    func updateNSView(_ nsView: NSProgressIndicator, context: NSViewRepresentableContext<ActivityIndicator>) {
        isAnimating ? nsView.startAnimation(nil) : nsView.stopAnimation(nil)
    }
    
}

Usage is as follows:

ActivityIndicator(isAnimating: .constant(true), style: .spinning)

Upvotes: 2

Despotovic
Despotovic

Reputation: 1962

A convenient way in SwiftUI that I found useful is 2 step approach:

  1. Create a ViewModifier that will embed your view into ZStack and add progress indicator on top. Could be something like this:

     struct LoadingIndicator: ViewModifier {
     let width = UIScreen.main.bounds.width * 0.3
     let height =  UIScreen.main.bounds.width * 0.3
    
     func body(content: Content) -> some View {
         return ZStack {
             content
                 .disabled(true)
                 .blur(radius: 2)
    
             //gray background
             VStack{}
                 .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
                 .background(Color.gray.opacity(0.2))
                 .cornerRadius(20)
                 .edgesIgnoringSafeArea(.all)
    
             //progress indicator
             ProgressView()
                 .frame(width: width, height: height)
                 .background(Color.white)
                 .cornerRadius(20)
                 .opacity(1)
                 .shadow(color: Color.gray.opacity(0.5), radius: 4.0, x: 1.0, y: 2.0)
           }
    }
    
  2. Create view extension that will make conditional modifier application available to any view:

     extension View {
     /// Applies the given transform if the given condition evaluates to `true`.
     /// - Parameters:
     ///   - condition: The condition to evaluate.
     ///   - transform: The transform to apply to the source `View`.
     /// - Returns: Either the original `View` or the modified `View` if the condition is `true`.
     @ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
         if condition {
             transform(self)
         } else {
             self
         }
       }
    }
    
  3. Usage is very intuitive. Suppose that myView() returns whatever your view is. You just conditionally apply the modifier using .if view extension from step 2:

     var body: some View {
         myView()
           .if(myViewModel.isLoading){ view in
             view.modifier(LoadingIndicator())
         }
     }
    

In case that myViewModel.isLoading is false, no modifier will be applied, so loading indicator won't show.

Of course, you can use any kind of progress indicator you wish - default or your own custom one.

Upvotes: 5

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119917

iOS 14

it's just a simple view.

ProgressView()

Currently, it's defaulted to CircularProgressViewStyle but you can manually set the style of it by adding the following modifer:

.progressViewStyle(CircularProgressViewStyle())

Also, the style could be anything that conforms to ProgressViewStyle


iOS 13 and above

Fully customizable Standard UIActivityIndicator in SwiftUI: (Exactly as a native View):

You can build and configure it (as much as you could in the original UIKit):

ActivityIndicator(isAnimating: loading)
    .configure { $0.color = .yellow } // Optional configurations (🎁 bouns)
    .background(Color.blue)

Result


Just implement this base struct and you will be good to go:

struct ActivityIndicator: UIViewRepresentable {
    
    typealias UIView = UIActivityIndicatorView
    var isAnimating: Bool
    fileprivate var configuration = { (indicator: UIView) in }

    func makeUIView(context: UIViewRepresentableContext<Self>) -> UIView { UIView() }
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<Self>) {
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
        configuration(uiView)
    }
}

🎁 Bouns Extension:

With this little helpful extension, you can access the configuration through a modifier like other SwiftUI views:

extension View where Self == ActivityIndicator {
    func configure(_ configuration: @escaping (Self.UIView)->Void) -> Self {
        Self.init(isAnimating: self.isAnimating, configuration: configuration)
    }
}

The classic way:

Also you can configure the view in a classic initializer:

ActivityIndicator(isAnimating: loading) { 
    $0.color = .red
    $0.hidesWhenStopped = false
    //Any other UIActivityIndicatorView property you like
}

This method is fully adaptable. For example, you can see How to make TextField become the first responder with the same method here

Upvotes: 151

batuhankrbb
batuhankrbb

Reputation: 706

It's really easy with SwiftUI 2.0 I made this simple and easy custom view with ProgressView

Here is how it looks:

enter image description here

Code:

import SwiftUI

struct ActivityIndicatorView: View {
    @Binding var isPresented:Bool
    var body: some View {
        if isPresented{
            ZStack{
                RoundedRectangle(cornerRadius: 15).fill(CustomColor.gray.opacity(0.1))
                ProgressView {
                    Text("Loading...")
                        .font(.title2)
                }
            }.frame(width: 120, height: 120, alignment: .center)
            .background(RoundedRectangle(cornerRadius: 25).stroke(CustomColor.gray,lineWidth: 2))
        }
    }
}

Upvotes: 2

ingconti
ingconti

Reputation: 11666

my 2 cents for nice and simpler code of batuhankrbb, showing use of isPresented in timer... or other stuff... (I will use it in url callback..)

//
//  ContentView.swift
//
//  Created by ing.conti on 27/01/21.


import SwiftUI

struct ActivityIndicatorView: View {
    @Binding var isPresented:Bool
    var body: some View {
        if isPresented{
            ZStack{
                RoundedRectangle(cornerRadius: 15).fill(Color.gray.opacity(0.1))
                ProgressView {
                    Text("Loading...")
                        .font(.title2)
                }
            }.frame(width: 120, height: 120, alignment: .center)
            .background(RoundedRectangle(cornerRadius: 25).stroke(Color.gray,lineWidth: 2))
        }
    }
}



struct ContentView: View {
    @State var isPresented = false
    @State var counter = 0
    var body: some View {
        
        VStack{
            Text("Hello, world! \(counter)")
                .padding()
            
            ActivityIndicatorView(isPresented: $isPresented)
        }.onAppear(perform: {
            _ = startRefreshing()
        })
    }
    
    
    
    func startRefreshing()->Timer{
        
        let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            
            counter+=1
            print(counter)
            if counter>2{
                isPresented = true
            }
            
            if counter>4{
                isPresented = false
                timer.invalidate()
            }
        }
        return timer
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Upvotes: 1

Arvind Patel
Arvind Patel

Reputation: 501

struct ContentView: View {
    
    @State private var isCircleRotating = true
    @State private var animateStart = false
    @State private var animateEnd = true
    
    var body: some View {
        
        ZStack {
            Circle()
                .stroke(lineWidth: 10)
                .fill(Color.init(red: 0.96, green: 0.96, blue: 0.96))
                .frame(width: 150, height: 150)
            
            Circle()
                .trim(from: animateStart ? 1/3 : 1/9, to: animateEnd ? 2/5 : 1)
                .stroke(lineWidth: 10)
                .rotationEffect(.degrees(isCircleRotating ? 360 : 0))
                .frame(width: 150, height: 150)
                .foregroundColor(Color.blue)
                .onAppear() {
                    withAnimation(Animation
                                    .linear(duration: 1)
                                    .repeatForever(autoreverses: false)) {
                        self.isCircleRotating.toggle()
                    }
                    withAnimation(Animation
                                    .linear(duration: 1)
                                    .delay(0.5)
                                    .repeatForever(autoreverses: true)) {
                        self.animateStart.toggle()
                    }
                    withAnimation(Animation
                                    .linear(duration: 1)
                                    .delay(1)
                                    .repeatForever(autoreverses: true)) {
                        self.animateEnd.toggle()
                    }
                }
        }
    }
}

enter image description here

Upvotes: 14

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119917

Custom Indicators

Although Apple supports native Activity Indicator now from the SwiftUI 2.0, You can Simply implement your own animations. These are all supported on SwiftUI 1.0. Also it is working in widgets.

Arcs

struct Arcs: View {
    @Binding var isAnimating: Bool
    let count: UInt
    let width: CGFloat
    let spacing: CGFloat

    var body: some View {
        GeometryReader { geometry in
            ForEach(0..<Int(count)) { index in
                item(forIndex: index, in: geometry.size)
                    .rotationEffect(isAnimating ? .degrees(360) : .degrees(0))
                    .animation(
                        Animation.default
                            .speed(Double.random(in: 0.2...0.5))
                            .repeatCount(isAnimating ? .max : 1, autoreverses: false)
                    )
            }
        }
        .aspectRatio(contentMode: .fit)
    }

    private func item(forIndex index: Int, in geometrySize: CGSize) -> some View {
        Group { () -> Path in
            var p = Path()
            p.addArc(center: CGPoint(x: geometrySize.width/2, y: geometrySize.height/2),
                     radius: geometrySize.width/2 - width/2 - CGFloat(index) * (width + spacing),
                     startAngle: .degrees(0),
                     endAngle: .degrees(Double(Int.random(in: 120...300))),
                     clockwise: true)
            return p.strokedPath(.init(lineWidth: width))
        }
        .frame(width: geometrySize.width, height: geometrySize.height)
    }
}

Demo of different variations Arcs


Bars

struct Bars: View {
    @Binding var isAnimating: Bool
    let count: UInt
    let spacing: CGFloat
    let cornerRadius: CGFloat
    let scaleRange: ClosedRange<Double>
    let opacityRange: ClosedRange<Double>

    var body: some View {
        GeometryReader { geometry in
            ForEach(0..<Int(count)) { index in
                item(forIndex: index, in: geometry.size)
            }
        }
        .aspectRatio(contentMode: .fit)
    }

    private var scale: CGFloat { CGFloat(isAnimating ? scaleRange.lowerBound : scaleRange.upperBound) }
    private var opacity: Double { isAnimating ? opacityRange.lowerBound : opacityRange.upperBound }

    private func size(count: UInt, geometry: CGSize) -> CGFloat {
        (geometry.width/CGFloat(count)) - (spacing-2)
    }

    private func item(forIndex index: Int, in geometrySize: CGSize) -> some View {
        RoundedRectangle(cornerRadius: cornerRadius,  style: .continuous)
            .frame(width: size(count: count, geometry: geometrySize), height: geometrySize.height)
            .scaleEffect(x: 1, y: scale, anchor: .center)
            .opacity(opacity)
            .animation(
                Animation
                    .default
                    .repeatCount(isAnimating ? .max : 1, autoreverses: true)
                    .delay(Double(index) / Double(count) / 2)
            )
            .offset(x: CGFloat(index) * (size(count: count, geometry: geometrySize) + spacing))
    }
}

Demo of different variations Bars


Blinkers

struct Blinking: View {
    @Binding var isAnimating: Bool
    let count: UInt
    let size: CGFloat

    var body: some View {
        GeometryReader { geometry in
            ForEach(0..<Int(count)) { index in
                item(forIndex: index, in: geometry.size)
                    .frame(width: geometry.size.width, height: geometry.size.height)

            }
        }
        .aspectRatio(contentMode: .fit)
    }

    private func item(forIndex index: Int, in geometrySize: CGSize) -> some View {
        let angle = 2 * CGFloat.pi / CGFloat(count) * CGFloat(index)
        let x = (geometrySize.width/2 - size/2) * cos(angle)
        let y = (geometrySize.height/2 - size/2) * sin(angle)
        return Circle()
            .frame(width: size, height: size)
            .scaleEffect(isAnimating ? 0.5 : 1)
            .opacity(isAnimating ? 0.25 : 1)
            .animation(
                Animation
                    .default
                    .repeatCount(isAnimating ? .max : 1, autoreverses: true)
                    .delay(Double(index) / Double(count) / 2)
            )
            .offset(x: x, y: y)
    }
}

Demo of different variations Blinkers


For the sake of preventing walls of code, you can find more elegant indicators in this repo hosted on the git.

Note that all these animations have a Binding that MUST toggle to be run.

Upvotes: 36

Matteo Pacini
Matteo Pacini

Reputation: 22846

As of Xcode 12 beta (iOS 14), a new view called ProgressView is available to developers, and that can display both determinate and indeterminate progress.

Its style defaults to CircularProgressViewStyle, which is exactly what we're looking for.

var body: some View {
    VStack {
        ProgressView()
           // and if you want to be explicit / future-proof...
           // .progressViewStyle(CircularProgressViewStyle())
    }
}

Xcode 11.x

Quite a few views are not yet represented in SwiftUI, but it's easily to port them into the system. You need to wrap UIActivityIndicator and make it UIViewRepresentable.

(More about this can be found in the excellent WWDC 2019 talk - Integrating SwiftUI)

struct ActivityIndicator: UIViewRepresentable {

    @Binding var isAnimating: Bool
    let style: UIActivityIndicatorView.Style

    func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
        return UIActivityIndicatorView(style: style)
    }

    func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
    }
}

Then you can use it as follows - here's an example of a loading overlay.

Note: I prefer using ZStack, rather than overlay(:_), so I know exactly what's going on in my implementation.

struct LoadingView<Content>: View where Content: View {

    @Binding var isShowing: Bool
    var content: () -> Content

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .center) {

                self.content()
                    .disabled(self.isShowing)
                    .blur(radius: self.isShowing ? 3 : 0)

                VStack {
                    Text("Loading...")
                    ActivityIndicator(isAnimating: .constant(true), style: .large)
                }
                .frame(width: geometry.size.width / 2,
                       height: geometry.size.height / 5)
                .background(Color.secondary.colorInvert())
                .foregroundColor(Color.primary)
                .cornerRadius(20)
                .opacity(self.isShowing ? 1 : 0)

            }
        }
    }

}

To test it, you can use this example code:

struct ContentView: View {

    var body: some View {
        LoadingView(isShowing: .constant(true)) {
            NavigationView {
                List(["1", "2", "3", "4", "5"], id: \.self) { row in
                    Text(row)
                }.navigationBarTitle(Text("A List"), displayMode: .large)
            }
        }
    }

}

Result:

enter image description here

Upvotes: 484

Pedro Trujillo
Pedro Trujillo

Reputation: 1691

Try this:

import SwiftUI

struct LoadingPlaceholder: View {
    var text = "Loading..."
    init(text:String ) {
        self.text = text
    }
    var body: some View {
        VStack(content: {
            ProgressView(self.text)
        })
    }
}

More information about at SwiftUI ProgressView

Upvotes: 1

Yisselda
Yisselda

Reputation: 71

I implemented the classic UIKit indicator using SwiftUI. See the activity indicator in action here

struct ActivityIndicator: View {
  @State private var currentIndex: Int = 0

  func incrementIndex() {
    currentIndex += 1
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50), execute: {
      self.incrementIndex()
    })
  }

  var body: some View {
    GeometryReader { (geometry: GeometryProxy) in
      ForEach(0..<12) { index in
        Group {
          Rectangle()
            .cornerRadius(geometry.size.width / 5)
            .frame(width: geometry.size.width / 8, height: geometry.size.height / 3)
            .offset(y: geometry.size.width / 2.25)
            .rotationEffect(.degrees(Double(-360 * index / 12)))
            .opacity(self.setOpacity(for: index))
        }.frame(width: geometry.size.width, height: geometry.size.height)
      }
    }
    .aspectRatio(1, contentMode: .fit)
    .onAppear {
      self.incrementIndex()
    }
  }

  func setOpacity(for index: Int) -> Double {
    let opacityOffset = Double((index + currentIndex - 1) % 11 ) / 12 * 0.9
    return 0.1 + opacityOffset
  }
}

struct ActivityIndicator_Previews: PreviewProvider {
  static var previews: some View {
    ActivityIndicator()
      .frame(width: 50, height: 50)
      .foregroundColor(.blue)
  }
}

Upvotes: 4

moyoteg
moyoteg

Reputation: 357

In addition to Mojatba Hosseini's answer,

I've made a few updates so that this can be put in a swift package:

Activity indicator:

import Foundation
import SwiftUI
import UIKit

public struct ActivityIndicator: UIViewRepresentable {

  public typealias UIView = UIActivityIndicatorView
  public var isAnimating: Bool = true
  public var configuration = { (indicator: UIView) in }

 public init(isAnimating: Bool, configuration: ((UIView) -> Void)? = nil) {
    self.isAnimating = isAnimating
    if let configuration = configuration {
        self.configuration = configuration
    }
 }

 public func makeUIView(context: UIViewRepresentableContext<Self>) -> UIView {
    UIView()
 }

 public func updateUIView(_ uiView: UIView, context: 
    UIViewRepresentableContext<Self>) {
     isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
     configuration(uiView)
}}

Extension:

public extension View where Self == ActivityIndicator {
func configure(_ configuration: @escaping (Self.UIView) -> Void) -> Self {
    Self.init(isAnimating: self.isAnimating, configuration: configuration)
 }
}

Upvotes: 3

Manish
Manish

Reputation: 29

// Activity View

struct ActivityIndicator: UIViewRepresentable {

    let style: UIActivityIndicatorView.Style
    @Binding var animate: Bool

    private let spinner: UIActivityIndicatorView = {
        $0.hidesWhenStopped = true
        return $0
    }(UIActivityIndicatorView(style: .medium))

    func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
        spinner.style = style
        return spinner
    }

    func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
        animate ? uiView.startAnimating() : uiView.stopAnimating()
    }

    func configure(_ indicator: (UIActivityIndicatorView) -> Void) -> some View {
        indicator(spinner)
        return self
    }   
}

// Usage
struct ContentView: View {

    @State var animate = false

    var body: some View {
            ActivityIndicator(style: .large, animate: $animate)
                .configure {
                    $0.color = .red
            }
            .background(Color.blue)
    }
}

Upvotes: 0

Rashid Latif
Rashid Latif

Reputation: 2901

Activity indicator in SwiftUI


import SwiftUI

struct Indicator: View {

    @State var animateTrimPath = false
    @State var rotaeInfinity = false

    var body: some View {

        ZStack {
            Color.black
                .edgesIgnoringSafeArea(.all)
            ZStack {
                Path { path in
                    path.addLines([
                        .init(x: 2, y: 1),
                        .init(x: 1, y: 0),
                        .init(x: 0, y: 1),
                        .init(x: 1, y: 2),
                        .init(x: 3, y: 0),
                        .init(x: 4, y: 1),
                        .init(x: 3, y: 2),
                        .init(x: 2, y: 1)
                    ])
                }
                .trim(from: animateTrimPath ? 1/0.99 : 0, to: animateTrimPath ? 1/0.99 : 1)
                .scale(50, anchor: .topLeading)
                .stroke(Color.yellow, lineWidth: 20)
                .offset(x: 110, y: 350)
                .animation(Animation.easeInOut(duration: 1.5).repeatForever(autoreverses: true))
                .onAppear() {
                    self.animateTrimPath.toggle()
                }
            }
            .rotationEffect(.degrees(rotaeInfinity ? 0 : -360))
            .scaleEffect(0.3, anchor: .center)
            .animation(Animation.easeInOut(duration: 1.5)
            .repeatForever(autoreverses: false))
            .onAppear(){
                self.rotaeInfinity.toggle()
            }
        }
    }
}

struct Indicator_Previews: PreviewProvider {
    static var previews: some View {
        Indicator()
    }
}

Activity indicator in SwiftUI

Upvotes: 5

Related Questions