From ee786bea91e6c0d064fbce07b097cffea145a370 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 28 Jan 2020 21:11:18 +0800 Subject: [PATCH] Update the Example with watchOS's native indicator (thanks @JagCesar), simplify the code --- .../project.pbxproj | 30 +-- .../SDWebImageSwiftUIDemo/ActivityBar.swift | 37 --- .../SDWebImageSwiftUIDemo/ContentView.swift | 42 ++-- .../SDWebImageSwiftUIDemo/DetailView.swift | 17 +- Example/SDWebImageSwiftUIDemo/Espera.swift | 213 ++++++++++++++++++ .../SDWebImageSwiftUIDemo/ProgressBar.swift | 28 --- README.md | 1 + 7 files changed, 248 insertions(+), 120 deletions(-) delete mode 100644 Example/SDWebImageSwiftUIDemo/ActivityBar.swift create mode 100644 Example/SDWebImageSwiftUIDemo/Espera.swift delete mode 100644 Example/SDWebImageSwiftUIDemo/ProgressBar.swift diff --git a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 3338b29..3c33ff4 100644 --- a/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/Example/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -14,7 +14,10 @@ 320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; }; 320CDC3522FADB45007CF858 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */; }; 320CDC3822FADB45007CF858 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */; }; - 321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; }; + 3243598423E05C3D006DF9C5 /* Espera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3243598323E05C3D006DF9C5 /* Espera.swift */; }; + 3243598523E05C3D006DF9C5 /* Espera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3243598323E05C3D006DF9C5 /* Espera.swift */; }; + 3243598623E05C3D006DF9C5 /* Espera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3243598323E05C3D006DF9C5 /* Espera.swift */; }; + 3243598723E05C3D006DF9C5 /* Espera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3243598323E05C3D006DF9C5 /* Espera.swift */; }; 326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; }; 32E529102348A0C900EA46FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E5290F2348A0C900EA46FF /* Assets.xcassets */; }; @@ -34,17 +37,10 @@ 32E529552348A0DF00EA46FF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E529542348A0DF00EA46FF /* Preview Assets.xcassets */; }; 32E529622348A10B00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32E529632348A10B00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; - 32E529642348A10B00EA46FF /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; }; 32E529652348A10B00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32E529662348A10B00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; - 32E529672348A10B00EA46FF /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; }; 32E529682348A10C00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; }; 32E529692348A10C00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; }; - 32E5296A2348A10C00EA46FF /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */; }; - 32E7F121236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; - 32E7F122236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; - 32E7F123236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; - 32E7F124236CAAB8001688BC /* ActivityBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E7F120236CAAB8001688BC /* ActivityBar.swift */; }; 68543C9252A5BD46E9573195 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79C3538209F8065DCCFBE205 /* Pods_SDWebImageSwiftUIDemo_tvOS.framework */; }; 8E29022B4DCBF0EFF9CF82F9 /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E25DB0256669F3B7EE7C566D /* Pods_SDWebImageSwiftUIDemo_watchOS_WatchKit_Extension.framework */; }; E61581A5A1063B0E6795157D /* Pods_SDWebImageSwiftUIDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0FCDD95C695D2F914DC9B3B /* Pods_SDWebImageSwiftUIDemo.framework */; }; @@ -102,7 +98,7 @@ 320CDC3422FADB45007CF858 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; + 3243598323E05C3D006DF9C5 /* Espera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Espera.swift; sourceTree = ""; }; 326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; 32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32E5290B2348A0C700EA46FF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -128,7 +124,6 @@ 32E529512348A0DF00EA46FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32E529542348A0DF00EA46FF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 32E529562348A0DF00EA46FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 32E7F120236CAAB8001688BC /* ActivityBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityBar.swift; sourceTree = ""; }; 3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54859B427E0A79E823713963 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; @@ -217,8 +212,7 @@ 320CDC2D22FADB44007CF858 /* SceneDelegate.swift */, 320CDC2F22FADB44007CF858 /* ContentView.swift */, 326B0D702345C01900D28269 /* DetailView.swift */, - 321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */, - 32E7F120236CAAB8001688BC /* ActivityBar.swift */, + 3243598323E05C3D006DF9C5 /* Espera.swift */, 320CDC3122FADB45007CF858 /* Assets.xcassets */, 320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */, 320CDC3922FADB45007CF858 /* Info.plist */, @@ -798,8 +792,7 @@ 320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */, 326B0D712345C01900D28269 /* DetailView.swift in Sources */, 320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */, - 321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */, - 32E7F121236CAAB8001688BC /* ActivityBar.swift in Sources */, + 3243598423E05C3D006DF9C5 /* Espera.swift in Sources */, 320CDC3022FADB44007CF858 /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -810,9 +803,8 @@ files = ( 32E529622348A10B00EA46FF /* ContentView.swift in Sources */, 32E529632348A10B00EA46FF /* DetailView.swift in Sources */, - 32E529642348A10B00EA46FF /* ProgressBar.swift in Sources */, - 32E7F122236CAAB8001688BC /* ActivityBar.swift in Sources */, 32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */, + 3243598523E05C3D006DF9C5 /* Espera.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -822,9 +814,8 @@ files = ( 32E529652348A10B00EA46FF /* ContentView.swift in Sources */, 32E529662348A10B00EA46FF /* DetailView.swift in Sources */, - 32E529672348A10B00EA46FF /* ProgressBar.swift in Sources */, - 32E7F123236CAAB8001688BC /* ActivityBar.swift in Sources */, 32E529232348A0D300EA46FF /* AppDelegate.swift in Sources */, + 3243598623E05C3D006DF9C5 /* Espera.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -835,8 +826,7 @@ 32E5294E2348A0DE00EA46FF /* HostingController.swift in Sources */, 32E529692348A10C00EA46FF /* DetailView.swift in Sources */, 32E529502348A0DE00EA46FF /* ExtensionDelegate.swift in Sources */, - 32E5296A2348A10C00EA46FF /* ProgressBar.swift in Sources */, - 32E7F124236CAAB8001688BC /* ActivityBar.swift in Sources */, + 3243598723E05C3D006DF9C5 /* Espera.swift in Sources */, 32E529682348A10C00EA46FF /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SDWebImageSwiftUIDemo/ActivityBar.swift b/Example/SDWebImageSwiftUIDemo/ActivityBar.swift deleted file mode 100644 index f49004d..0000000 --- a/Example/SDWebImageSwiftUIDemo/ActivityBar.swift +++ /dev/null @@ -1,37 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SwiftUI - -/// A dot circle view that depicts the active status of a task. -struct ActivityBar: View { - private var dotRadius: CGFloat = 5 - @State private var isAnimating: Bool = false - - var body: some View { - GeometryReader { (geometry: GeometryProxy) in - ForEach(0..<5) { index in - Group { - Circle() - .frame(width: self.dotRadius, height: self.dotRadius) - .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 - } - } -} diff --git a/Example/SDWebImageSwiftUIDemo/ContentView.swift b/Example/SDWebImageSwiftUIDemo/ContentView.swift index 46b0828..aa31abd 100644 --- a/Example/SDWebImageSwiftUIDemo/ContentView.swift +++ b/Example/SDWebImageSwiftUIDemo/ContentView.swift @@ -10,6 +10,7 @@ import SwiftUI import SDWebImage import SDWebImageSwiftUI +// Allows `String` in `ForEach` extension String : Identifiable { public typealias ID = Int public var id: Int { @@ -17,6 +18,27 @@ extension String : Identifiable { } } +#if os(watchOS) +// watchOS does not provide built-in indicator, use Espera's custom indicator +extension Indicator where T == LoadingFlowerView { + /// Activity Indicator + public static var activity: Indicator { + Indicator { isAnimating, _ in + LoadingFlowerView() + } + } +} + +extension Indicator where T == StretchProgressView { + /// Progress Indicator + public static var progress: Indicator { + Indicator { isAnimating, progress in + StretchProgressView(progress: progress) + } + } +} +#endif + struct ContentView: View { @State var imageURLs = [ "http://assets.sbnation.com/assets/2512203/dogflops.gif", @@ -107,18 +129,13 @@ struct ContentView: View { #else WebImage(url: URL(string:url), isAnimating: self.$animated) .resizable() - .indicator { _, _ in - ActivityBar() - .foregroundColor(Color.white) - .frame(width: 50, height: 50) - } + .indicator(.activity) .animation(.easeInOut(duration: 0.5)) .transition(.fade) .scaledToFit() .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) #endif } else { - #if os(macOS) || os(iOS) || os(tvOS) WebImage(url: URL(string:url)) .resizable() /** @@ -131,19 +148,6 @@ struct ContentView: View { .transition(.fade) .scaledToFit() .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - #else - WebImage(url: URL(string:url)) - .resizable() - .indicator { _, _ in - ActivityBar() - .foregroundColor(Color.white) - .frame(width: 50, height: 50) - } - .animation(.easeInOut(duration: 0.5)) - .transition(.fade) - .scaledToFit() - .frame(width: CGFloat(100), height: CGFloat(100), alignment: .center) - #endif } Text((url as NSString).lastPathComponent) } diff --git a/Example/SDWebImageSwiftUIDemo/DetailView.swift b/Example/SDWebImageSwiftUIDemo/DetailView.swift index 9d32934..f6e0404 100644 --- a/Example/SDWebImageSwiftUIDemo/DetailView.swift +++ b/Example/SDWebImageSwiftUIDemo/DetailView.swift @@ -97,29 +97,14 @@ struct DetailView: View { #else WebImage(url: URL(string:url), options: [.progressiveLoad], isAnimating: $isAnimating) .resizable() - .indicator { isAnimating, progress in - ProgressBar(value: progress) - .foregroundColor(.blue) - .frame(maxHeight: 6) - } + .indicator(.progress) .scaledToFit() #endif } else { - #if os(macOS) || os(iOS) || os(tvOS) WebImage(url: URL(string:url), options: [.progressiveLoad]) .resizable() .indicator(.progress) .scaledToFit() - #else - WebImage(url: URL(string:url), options: [.progressiveLoad]) - .resizable() - .indicator { isAnimating, progress in - ProgressBar(value: progress) - .foregroundColor(.blue) - .frame(maxHeight: 6) - } - .scaledToFit() - #endif } } } diff --git a/Example/SDWebImageSwiftUIDemo/Espera.swift b/Example/SDWebImageSwiftUIDemo/Espera.swift new file mode 100644 index 0000000..0ddb0c6 --- /dev/null +++ b/Example/SDWebImageSwiftUIDemo/Espera.swift @@ -0,0 +1,213 @@ +// +// Espera.swift +// Espera +// +// Created by jagcesar on 2019-12-29. +// Copyright © 2019 Ambi. All rights reserved. +// + +import SwiftUI + +public struct RotatingCircleWithGap: View { + @State private var angle: Double = 270 + @State var isAnimating = false + private let lineWidth: CGFloat = 2 + + var foreverAnimation: Animation { + Animation.linear(duration: 1) + .repeatForever(autoreverses: false) + } + + public init() { } + + public var body: some View { + Circle() + .trim(from: 0.15, to: 1) + .stroke(Color.gray, style: StrokeStyle(lineWidth: self.lineWidth, lineCap: .round, lineJoin: CGLineJoin.round)) + .rotationEffect((Angle(degrees: self.isAnimating ? 360.0 : 0))) + .padding(EdgeInsets(top: lineWidth/2, leading: lineWidth/2, bottom: lineWidth/2, trailing: lineWidth/2)) + .animation(foreverAnimation) + .onAppear { + self.isAnimating = true + } + } +} + +private struct LoadingCircle: View { + let circleColor: Color + let scale: CGFloat + private let circleWidth: CGFloat = 8 + + var body: some View { + Circle() + .fill(circleColor) + .frame(width: circleWidth, height: circleWidth, alignment: .center) + .scaleEffect(scale) + } +} + +public struct LoadingFlowerView: View { + private let animationDuration: Double = 0.6 + private var singleCircleAnimationDuration: Double { + return animationDuration/3 + } + private var foreverAnimation: Animation { + Animation.linear(duration: animationDuration) + .repeatForever(autoreverses: true) + } + + @State private var color: Color = .init(white: 0.3) + @State private var scale: CGFloat = 0.98 + + public init() { } + + public var body: some View { + HStack(spacing: 1) { + VStack(spacing: 2) { + LoadingCircle(circleColor: color, scale: scale) + .animation(foreverAnimation.delay(singleCircleAnimationDuration*5)) + LoadingCircle(circleColor: color, scale: scale) + .animation(foreverAnimation.delay(singleCircleAnimationDuration*4)) + } + VStack(alignment: .center, spacing: 1) { + LoadingCircle(circleColor: color, scale: scale) + .animation(foreverAnimation) + LoadingCircle(circleColor: .clear, scale: 1) + LoadingCircle(circleColor: color, scale: scale) + .animation(foreverAnimation.delay(singleCircleAnimationDuration*3)) + } + VStack(alignment: .center, spacing: 2) { + LoadingCircle(circleColor: color, scale: scale) + .animation(foreverAnimation.delay(singleCircleAnimationDuration*1)) + LoadingCircle(circleColor: color, scale: scale) + .animation(foreverAnimation.delay(singleCircleAnimationDuration*2)) + } + } + .onAppear { + self.color = .white + self.scale = 1.02 + } + } +} + +private class StretchyShapeModel { + var forwards = true +} + +extension StretchyShape { + enum Side { + case front, back + } + + enum Mode { + case lagged, stretchy + } +} + +private struct StretchyShape: Shape { + + var progress: Double + var mode: Mode + init(progress: Double, mode: Mode = .lagged) { + self.progress = progress + self.mode = mode + } + + private var model = StretchyShapeModel() + + func path(in rect: CGRect) -> Path { + Path { path in + + addSide(.back, to: &path, rect: rect) + addSide(.front, to: &path, rect: rect) + + if progress >= 1 { + model.forwards.toggle() + } + } + } + + var animatableData: Double { + set { progress = newValue } + get { progress } + } + + private func easeInOutQuad(_ x: CGFloat) -> CGFloat { + if x <= 0.5 { + return pow(x, 2) * 2 + } + + let x = x - 0.5 + return 2 * x * (1 - x) + 0.5 + } + + private func addSide(_ side: Side, to path: inout Path, rect: CGRect) { + let lag = 0.1 + + let laggedProgress: CGFloat + let startAngle: Angle + let endAngle: Angle + switch side { + case .front: + laggedProgress = CGFloat(progress + lag) + startAngle = Angle(degrees: 90) + endAngle = Angle(degrees: -90) + case .back: + if mode == .stretchy { + laggedProgress = 0 + } else { + laggedProgress = CGFloat(progress - lag) + } + startAngle = Angle(degrees: -90) + endAngle = Angle(degrees: 90) + } + + var progress = max(0, min(1, laggedProgress)) + + if !model.forwards { + progress = 1 - progress + } + + let radius = rect.height / 2 + let offset = easeInOutQuad(progress) * (rect.width - rect.height) + + path.addArc(center: CGPoint(x: radius + offset, y: radius), radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: model.forwards) + } +} + +public struct StretchLoadingView: View { + + @State private var progress: Double = 0 + + public init() { } + + public var body: some View { + StretchyShape(progress: progress) + .animation(Animation.linear(duration: 0.6).repeatForever(autoreverses: false)) + .onAppear { + withAnimation { + self.progress = 1 + } + } + } +} + +public struct StretchProgressView: View { + + @Binding public var progress: CGFloat + + public var body: some View { + StretchyShape(progress: Double(progress), mode: .stretchy) + .frame(width: 140, height: 10) + } +} + +struct Previews: PreviewProvider { + static var previews: some View { + Group { + RotatingCircleWithGap() + LoadingFlowerView() + StretchLoadingView().frame(width: 60, height: 14) + } + } +} diff --git a/Example/SDWebImageSwiftUIDemo/ProgressBar.swift b/Example/SDWebImageSwiftUIDemo/ProgressBar.swift deleted file mode 100644 index 14c231d..0000000 --- a/Example/SDWebImageSwiftUIDemo/ProgressBar.swift +++ /dev/null @@ -1,28 +0,0 @@ -/* -* This file is part of the SDWebImage package. -* (c) DreamPiggy -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -import SwiftUI - -/// A linear view that depicts the progress of a task over time. -public struct ProgressBar: View { - @Binding var value: CGFloat - - public var body: some View { - GeometryReader { geometry in - ZStack(alignment: .leading) { - Rectangle() - .frame(width: geometry.size.width) - .opacity(0.3) - Rectangle() - .frame(width: geometry.size.width * self.value) - .opacity(0.6) - } - } - .cornerRadius(2) - } -} diff --git a/README.md b/README.md index 9b2f0da..d514242 100644 --- a/README.md +++ b/README.md @@ -404,6 +404,7 @@ Which means, this project is one core use case and downstream dependency, which - [libwebp](https://github.com/SDWebImage/libwebp-Xcode) - [Kingfisher](https://github.com/onevcat/Kingfisher) - [SwiftUIX](https://github.com/SwiftUIX/SwiftUIX) +- [Espera](https://github.com/JagCesar/Espera) - [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect) - [ViewInspector](https://github.com/nalexn/ViewInspector)