【问题标题】:Activity indicator in SwiftUISwiftUI 中的活动指示器
【发布时间】:2019-10-23 02:21:02
【问题描述】:

尝试在 SwiftUI 中添加全屏活动指示器。

我可以在View 协议中使用.overlay(overlay: ) 函数。

有了这个,我可以制作任何视图覆盖,但我在SwiftUI 中找不到等效的iOS 默认样式UIActivityIndicatorView

如何使用SwiftUI 制作默认样式微调器?

注意:这不是在 UIKit 框架中添加活动指示器。

【问题讨论】:

  • 我也试过了,没找到,估计以后会加的:)
  • 确保使用反馈助手向 Apple 提交反馈问题。在 Beta 过程中尽早获取请求是了解您想要在框架中获得什么的最佳方式。

标签: swiftui


【解决方案1】:

Xcode 12 beta (iOS 14) 开始,一个名为 ProgressView 的新视图是 available to developers,它可以显示确定和不确定的进度。

它的样式默认为CircularProgressViewStyle,这正是我们正在寻找的。​​p>

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

Xcode 11.x

SwiftUI 中还没有显示相当多的视图,但是很容易将它们移植到系统中。 您需要将UIActivityIndicator 包装起来并使其变为UIViewRepresentable

(有关此内容的更多信息,请参阅精彩的 WWDC 2019 演讲 - 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()
    }
}

然后您可以按如下方式使用它 - 这是加载叠加层的示例。

注意:我更喜欢使用ZStack,而不是overlay(:_),所以我确切地知道我的实现中发生了什么。

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)

            }
        }
    }

}

要对其进行测试,您可以使用以下示例代码:

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)
            }
        }
    }

}

结果:

【讨论】:

  • 但是如何阻止呢?
  • 嗨@MatteoPacini,感谢您的回答。但是,请你帮我如何隐藏活动指示器。你能把这个代码写下来吗?
  • @Alfi 在他的代码中写着isShowing: .constant(true)。这意味着该指标始终显示。您需要做的是有一个@State 变量,当您希望加载指示器出现时(数据正在加载时)为真,然后当您希望加载指示器消失时将其更改为假(当数据是加载完成)。例如,如果变量名为 isDataLoading,您将使用 isShowing: $isDataLoading 而不是 Matteo 放置 isShowing: .constant(true) 的位置。
  • @MatteoPacini 您实际上不需要为此绑定,因为它没有在 ActivityIndi​​cator 或 LoadingView 中进行修改。只需一个常规的布尔变量即可。当您想要修改视图内的变量并将该更改传递回父级时,绑定非常有用。
  • @nelsonPARRILLA 我怀疑tintColor 仅适用于纯 Swift UI 视图 - 不适用于桥接 (UIViewRepresentable) 视图。
【解决方案2】:

iOS 14

这只是一个简单的视图。

ProgressView()

目前默认为CircularProgressViewStyle,但您可以通过添加以下修饰符手动设置其样式:

.progressViewStyle(CircularProgressViewStyle())

另外,样式可以是任何符合ProgressViewStyle的样式


iOS 13 及更高版本

在 SwiftUI 中完全可定制的标准 UIActivityIndicator:(与原生 View 完全相同):

您可以构建和配置它(在原始 UIKit 中尽可能多):

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


只需实现这个基础 struct 就可以了:

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)
    }
}

? 边界扩展:

有了这个有用的小扩展,你可以像其他 SwiftUI views 一样通过 modifier 访问配置:

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

经典方式:

您还可以在经典初始化程序中配置视图:

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

这种方法是完全适应的。比如你可以看到How to make TextField become the first responder用同样的方法here

【讨论】:

  • 如何更改 ProgressView 的颜色?
  • .progressViewStyle(CircularProgressViewStyle(tint: Color.red)) 会改变颜色
  • 您的“Bonus Extension: configure()”第二次调用 init,占用了内存。我对吗?还是它优化得如此之高,以至于我们被允许对 init 进行这样的链式调用?
  • 这是一种糖,对于这种情况来说这不是很贵,但我没有测量大视图的性能影响。您可以测量并将实现更改为更有效的方式(因为它是一个类),但初始化一个结构并没有那么昂贵
【解决方案3】:

如果您想要 swift-ui-style 解决方案,那么这就是魔法:

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
    }
    
}

简单易用:

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

希望对你有帮助!

示例用法:

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

【讨论】:

  • 这对我帮助很大,非常感谢!您可以定义函数来创建圆圈并为动画添加视图修饰符以使其更具可读性。
  • 喜欢这个解决方案!
  • 如果 isAnimating 是 State ,我将如何删除动画,可以用 @Binding 代替吗?
  • 最新 Xcode 和 Swift 中的错误:“编译器无法在合理的时间内对该表达式进行类型检查;尝试将表达式分解为不同的子表达式”
【解决方案4】:

自定义指标

虽然 Apple 现在从 SwiftUI 2.0 开始支持原生 Activity Indicator,但您可以简单地实现自己的动画。 SwiftUI 1.0 都支持这些。它也正在在小部件中工作。

弧线

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


条形

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


闪光灯

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


为了防止代码墙,您可以在this repo hosted on the git中找到更优雅的指标。

请注意,所有这些动画都有一个Binding必须切换才能运行。

【讨论】:

  • 这太棒了!不过我发现了一个错误 - iActivityIndicator(style: .rotatingShapes(count: 10, size: 15)) 有一个非常奇怪的动画
  • 顺便问一下iActivityIndicator().style(.rotatingShapes(count: 10, size: 15)) 有什么问题? @pawello2222 ?
  • 如果将count 设置为5 或更少,动画看起来很好(看起来类似于this answer)。但是,如果将 count 设置为 15,则前导点不会停在圆的 顶部。它开始执行另一个循环,然后返回 到顶部,然后再次开始循环。我不确定这是否是故意的。仅在模拟器上测试,Xcode 12.0.1。
  • 嗯。那是因为动画没有序列化。我应该为此在框架中添加一个序列化选项。感谢您分享您的意见。
  • @MojtabaHosseini 如何切换绑定以运行?
【解决方案5】:
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()
                    }
                }
        }
    }
}

【讨论】:

    【解决方案6】:

    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()
        }
    }
    
    

    【讨论】:

      【解决方案7】:

      我使用 SwiftUI 实现了经典的 UIKit 指示器。 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)
        }
      }
      
      

      【讨论】:

        【解决方案8】:

        除了Mojatba Hosseini's answer

        我进行了一些更新,以便可以将其放入 swift 包

        活动指标:

        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)
        }}
        

        扩展:

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

        【讨论】:

        • 这个怎么用?
        【解决方案9】:

        试试这个:

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

        更多关于 SwiftUI 的信息ProgressView

        【讨论】:

          【解决方案10】:

          使用 SwiftUI 2.0 真的很简单我用 ProgressView 制作了这个简单易用的自定义视图

          这是它的外观:

          代码:

          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))
                  }
              }
          }
          

          【讨论】:

            【解决方案11】:

            SwiftUI 中我发现有用的一种便捷方法是两步法:

            1. 创建一个ViewModifier,它将您的视图嵌入到ZStack,并在顶部添加进度指示器。可能是这样的:

               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. 创建视图扩展,使条件修饰符应用程序可用于任何视图:

               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. 使用非常直观。假设myView() 返回您的视图。您只需使用步骤 2 中的 .if 视图扩展有条件地应用修饰符:

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

            如果myViewModel.isLoading 为假,则不会应用任何修饰符,因此不会显示加载指示器。

            当然,您可以使用任何类型的进度指示器 - 默认或您自己的自定义。

            【讨论】:

              【解决方案12】:

              ProgressView().progressViewStyle 修饰符,您可以在其中更改活动指示器的样式。

              【讨论】:

                【解决方案13】:
                // 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)
                    }
                }
                

                【讨论】:

                  【解决方案14】:

                  我的 2 美分用于 batuhankrbb 的漂亮和简单的代码,展示了 isPresented 在计时器中的使用......或其他东西......(我将在 url 回调中使用它......)

                  //
                  //  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()
                      }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2020-05-19
                    • 1970-01-01
                    • 2020-03-16
                    • 2016-05-23
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-06-28
                    • 2012-06-08
                    • 2010-11-08
                    相关资源
                    最近更新 更多