尝试在SwiftUI中添加一个全屏活动指示器。
我可以在视图协议中使用.overlay(overlay:)函数。
有了这个,我可以做任何视图叠加,但我找不到iOS默认风格UIActivityIndicatorView等效的SwiftUI。
如何使用SwiftUI创建默认样式微调器?
注意:这不是关于在UIKit框架中添加活动指示器。
尝试在SwiftUI中添加一个全屏活动指示器。
我可以在视图协议中使用.overlay(overlay:)函数。
有了这个,我可以做任何视图叠加,但我找不到iOS默认风格UIActivityIndicatorView等效的SwiftUI。
如何使用SwiftUI创建默认样式微调器?
注意:这不是关于在UIKit框架中添加活动指示器。
当前回答
我的2美分的漂亮和更简单的代码的batuhankrbb,显示使用定时器中的ispresents…或者其他东西…(我将使用它在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()
}
}
其他回答
我使用SwiftUI实现了经典的UIKit指示器。 点击这里查看活动指示器的运行情况
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)
}
}
试试这个:
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的信息
我已经使用AppKit和SwiftUI修改了Matteo Pacini的macOS答案。这允许你在SwiftUI中使用NSProgressIndicator,同时保留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)
}
}
用法如下:
ActivityIndicator(isAnimating: .constant(true), style: .spinning)
在SwiftUI中,我发现一个方便的方法是两步方法:
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) } } 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 } } } 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()) } }
如果是myViewModel。isLoading为false,没有修饰符将被应用,因此加载指示器将不显示。
当然,您可以使用任何类型的进度指示器—默认的或您自己的自定义的。
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()
}
}
}
}
}