ตัวบ่งชี้กิจกรรมใน SwiftUI


97

กำลังพยายามเพิ่มตัวบ่งชี้กิจกรรมแบบเต็มหน้าจอใน SwiftUI

ฉันสามารถใช้.overlay(overlay: )ฟังก์ชันในViewโปรโตคอล

ด้วยวิธีนี้ฉันสามารถให้ภาพซ้อนทับมุมมองใด ๆ แต่ฉันไม่สามารถหาสิ่งที่ iOS ของรูปแบบเริ่มต้นเทียบเท่าUIActivityIndicatorViewSwiftUI

ฉันจะสร้างสปินเนอร์สไตล์เริ่มต้นด้วยได้SwiftUIอย่างไร

หมายเหตุ:นี่ไม่เกี่ยวกับการเพิ่มตัวบ่งชี้กิจกรรมในกรอบงาน UIKit


ฉันพยายามค้นหาแล้วและล้มเหลวเดาว่าจะถูกเพิ่มในภายหลัง :)
Markicevic

อย่าลืมส่งปัญหาคำติชมกับ Apple โดยใช้ตัวช่วยคำติชม การรับคำขอในช่วงต้นระหว่างขั้นตอนเบต้าเป็นวิธีที่ดีที่สุดในการดูสิ่งที่คุณต้องการในกรอบงาน
Jon

คำตอบ:


223

ตั้งแต่Xcode 12 beta ( iOS 14 ) มุมมองใหม่ที่เรียกว่าProgressViewมีให้สำหรับนักพัฒนาและสามารถแสดงความคืบหน้าทั้งที่กำหนดและไม่แน่นอน

สไตล์เริ่มต้นCircularProgressViewStyleซึ่งเป็นสิ่งที่เรากำลังมองหา

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

Xcode 11.x

ยังไม่มีการนำเสนอมุมมองค่อนข้างน้อยSwiftUIแต่สามารถโอนเข้าสู่ระบบได้อย่างง่ายดาย คุณจำเป็นต้องห่อและทำให้มันUIActivityIndicatorUIViewRepresentable

(เพิ่มเติมเกี่ยวกับเรื่องนี้สามารถพบได้ในการพูดคุยที่ยอดเยี่ยมของ WWDC 2019 - การผสานรวม 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)
            }
        }
    }

}

ผลลัพธ์:

ป้อนคำอธิบายภาพที่นี่


1
แต่จะหยุดได้อย่างไร?
Bagusflyer

1
สวัสดี @MatteoPacini ขอบคุณสำหรับคำตอบ แต่คุณช่วยฉันได้ไหมฉันจะซ่อนตัวบ่งชี้กิจกรรมได้อย่างไร กรุณาใส่รหัสสำหรับสิ่งนี้ได้ไหม
Annu

4
@Alfi isShowing: .constant(true)ในรหัสของเขาก็กล่าวว่า นั่นหมายความว่าตัวบ่งชี้จะแสดงอยู่เสมอ สิ่งที่คุณต้องทำคือมี@Stateตัวแปรที่เป็นจริงเมื่อคุณต้องการให้ตัวบ่งชี้การโหลดปรากฏขึ้น (เมื่อข้อมูลกำลังโหลด) จากนั้นเปลี่ยนเป็นเท็จเมื่อคุณต้องการให้ตัวบ่งชี้การโหลดหายไป (เมื่อโหลดข้อมูลเสร็จแล้ว) . ถ้าตัวแปรที่เรียกว่าisDataLoadingยกตัวอย่างเช่นคุณจะทำแทนมัตเตโอที่ใส่isShowing: $isDataLoading isShowing: .constant(true)
RPatel99

1
@MatteoPacini คุณไม่จำเป็นต้องมี Binding สำหรับสิ่งนี้เนื่องจากไม่มีการแก้ไขภายใน ActivityIndicator หรือใน LoadingView ตัวแปรบูลีนปกติทำงานได้ การผูกมีประโยชน์เมื่อคุณต้องการแก้ไขตัวแปรภายในมุมมองและส่งการเปลี่ยนแปลงกลับออกไปยังพาเรนต์
Helam

1
@nelsonPARRILLA ฉันสงสัยว่าtintColorใช้งานได้เฉพาะกับมุมมอง Swift UI ที่บริสุทธิ์เท่านั้นไม่ใช่ในมุมมอง bridged ( UIViewRepresentable)
Matteo Pacini

51

หากคุณต้องการวิธีการแก้ปัญหาแบบรวดเร็วนี่คือความมหัศจรรย์:

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(!self.isAnimating ? 1 - CGFloat(index) / 5 : 0.2 + CGFloat(index) / 5)
            .offset(y: geometry.size.width / 10 - geometry.size.height / 2)
          }.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
    }
  }
}

ใช้เพียง:

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

หวังว่าจะช่วยได้!

ตัวอย่างการใช้งาน:

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

ป้อนคำอธิบายภาพที่นี่


สิ่งนี้ช่วยฉันได้มากขอบคุณมาก! คุณสามารถกำหนดฟังก์ชันเพื่อสร้างแวดวงและเพิ่มตัวปรับมุมมองสำหรับภาพเคลื่อนไหวเพื่อให้อ่านง่ายขึ้น
Arif Ata Cengiz

2
รักโซลูชันนี้!
Jon Vogel

1
ฉันจะลบแอนิเมชั่นได้อย่างไรหาก isAnimating เป็นสถานะ @Binding แทนได้หรือไม่
Di Nerd

44

iOS 14 - เนทีฟ

มันเป็นเพียงมุมมองธรรมดา ๆ

ProgressView()

ปัจจุบันเป็นค่าเริ่มต้นCircularProgressViewStyleแต่คุณสามารถกำหนดรูปแบบของมันได้ด้วยตนเองโดยการเพิ่มโมดิเฟอร์ต่อไปนี้

.progressViewStyle(CircularProgressViewStyle())

นอกจากนี้สไตล์อาจเป็นอะไรก็ได้ที่สอดคล้องกัน ProgressViewStyle


iOS 13 - มาตรฐานที่ปรับแต่งได้อย่างสมบูรณ์UIActivityIndicatorใน SwiftUI: (เหมือนกับเนทีฟView):

คุณสามารถสร้างและกำหนดค่าได้ (เท่าที่ทำได้ในต้นฉบับUIKit):

ActivityIndicator(isAnimating: loading)
    .configure { $0.color = .yellow } // Optional configurations (🎁 bones)
    .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)
    }
}

🎁ส่วนขยายของกระดูก:

ด้วยส่วนขยายที่มีประโยชน์เล็กน้อยนี้คุณสามารถเข้าถึงการกำหนดค่าผ่านmodifierSwiftUI views อื่น ๆ:

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
}

วิธีนี้สามารถปรับใช้ได้อย่างเต็มที่ ตัวอย่างเช่นคุณสามารถดูวิธีทำให้ TextField กลายเป็นผู้ตอบกลับคนแรกด้วยวิธีการเดียวกันได้ที่นี่


จะเปลี่ยนสีของ ProgressView ได้อย่างไร?
Bagusflyer

.progressViewStyle(CircularProgressViewStyle(tint: Color.red))จะเปลี่ยนสี
Bagusflyer

การเรียก "Bonus Extension: configure ()" ของคุณเริ่มต้นเป็นครั้งที่สองโดยใช้หน่วยความจำ ฉันถูกไหม? หรือได้รับการปรับให้เหมาะสมอย่างมากจนเราได้รับอนุญาตให้ทำ chain invocation ของ init?
Ruzard

เป็นน้ำตาลราคาไม่แพงมากสำหรับกรณีนี้ แต่ฉันไม่ได้วัดประสิทธิภาพที่โดนสำหรับการดูจำนวนมาก คุณสามารถวัดและเปลี่ยนแปลงการนำไปใช้เป็นสิ่งที่มีประสิทธิภาพมากขึ้นได้ (เนื่องจากเป็นคลาส) แต่การเริ่มต้นโครงสร้างนั้นไม่แพงมากที่จะต้องกังวล
Mojtaba Hosseini

8

ตัวบ่งชี้ที่กำหนดเอง

แม้ว่า Apple จะรองรับตัวบ่งชี้กิจกรรมดั้งเดิมจาก SwiftUI 2.0 แล้ว แต่คุณสามารถใช้ภาพเคลื่อนไหวของคุณเองได้ ทั้งหมดนี้รองรับบน SwiftUI 1.0 นอกจากนี้ยังมีการทำงานในวิดเจ็ต

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

การสาธิตรูปแบบต่างๆ Arcs


บาร์

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

การสาธิตรูปแบบต่างๆ บาร์


ไฟกระพริบ

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

การสาธิตรูปแบบต่างๆ ไฟกระพริบ


เพื่อประโยชน์ในการป้องกันไม่ให้ผนังของรหัสที่คุณสามารถหาตัวชี้วัดสง่างามมากขึ้นในrepo นี้โฮสต์ไว้ในคอมไพล์

โปรดทราบว่าภาพเคลื่อนไหวทั้งหมดเหล่านี้มีสิ่งBindingที่ต้องสลับเพื่อให้ทำงานได้


นี่มันเยี่ยมมาก! ฉันพบข้อผิดพลาดอย่างหนึ่ง - มีอนิเมชั่นแปลก ๆ สำหรับiActivityIndicator(style: .rotatingShapes(count: 10, size: 15))
pawello2222

ปัญหาiActivityIndicator().style(.rotatingShapes(count: 10, size: 15))ระหว่างทางคืออะไร? @ pawello2222?
Mojtaba Hosseini

หากคุณตั้งค่าเป็นcount5 หรือน้อยกว่าภาพเคลื่อนไหวจะดูดี (ดูเหมือนคำตอบนี้ ) อย่างไรก็ตามหากคุณตั้งค่าเป็นcount15 จุดนำหน้าจะไม่หยุดอยู่ที่ด้านบนสุดของวงกลม มันเริ่มทำอีกรอบจากนั้นกลับมาที่ด้านบนแล้วเริ่มวงจรอีกครั้ง ฉันไม่แน่ใจว่าตั้งใจหรือเปล่า ทดสอบกับโปรแกรมจำลองเท่านั้น Xcode 12.0.1
pawello2222

อืม. นั่นเป็นเพราะภาพเคลื่อนไหวไม่ได้ถูกทำให้เป็นอนุกรม ฉันควรเพิ่มตัวเลือกการทำให้เป็นอนุกรมในกรอบสำหรับสิ่งนั้น ขอบคุณสำหรับการแบ่งปันความคิดเห็นของคุณ
Mojtaba Hosseini

@MojtabaHosseini คุณจะสลับการผูกเพื่อเรียกใช้อย่างไร
GarySabo

4

ฉันใช้ตัวบ่งชี้ UIKit แบบคลาสสิกโดยใช้ SwiftUI ดูตัวบ่งชี้กิจกรรมที่กำลังดำเนินการอยู่ที่นี่

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


3

นอกจากคำตอบของMojatba Hosseini แล้วแล้ว

ฉันได้ทำการอัปเดตเล็กน้อยเพื่อให้สามารถใส่ในแพ็คเกจที่รวดเร็ว :

ตัวบ่งชี้กิจกรรม:

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

2

ตัวบ่งชี้กิจกรรมใน 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()
    }
}

ตัวบ่งชี้กิจกรรมใน SwiftUI



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


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

ป้อนคำอธิบายภาพที่นี่


0

มันง่ายมากด้วย 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))
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.