Update the Example with watchOS's native indicator (thanks @JagCesar), simplify the code
This commit is contained in:
parent
65119ead92
commit
ee786bea91
|
@ -14,7 +14,10 @@
|
||||||
320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; };
|
320CDC3222FADB45007CF858 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3122FADB45007CF858 /* Assets.xcassets */; };
|
||||||
320CDC3522FADB45007CF858 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 320CDC3422FADB45007CF858 /* Preview 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 */; };
|
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 */; };
|
326B0D712345C01900D28269 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.swift */; };
|
||||||
32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; };
|
32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E5290B2348A0C700EA46FF /* AppDelegate.swift */; };
|
||||||
32E529102348A0C900EA46FF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E5290F2348A0C900EA46FF /* Assets.xcassets */; };
|
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 */; };
|
32E529552348A0DF00EA46FF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 32E529542348A0DF00EA46FF /* Preview Assets.xcassets */; };
|
||||||
32E529622348A10B00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; };
|
32E529622348A10B00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; };
|
||||||
32E529632348A10B00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.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 */; };
|
32E529652348A10B00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; };
|
||||||
32E529662348A10B00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.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 */; };
|
32E529682348A10C00EA46FF /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320CDC2F22FADB44007CF858 /* ContentView.swift */; };
|
||||||
32E529692348A10C00EA46FF /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326B0D702345C01900D28269 /* DetailView.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 */; };
|
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 */; };
|
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 */; };
|
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 = "<group>"; };
|
320CDC3422FADB45007CF858 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
320CDC3722FADB45007CF858 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
320CDC3922FADB45007CF858 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
|
3243598323E05C3D006DF9C5 /* Espera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Espera.swift; sourceTree = "<group>"; };
|
||||||
326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
|
326B0D702345C01900D28269 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
|
||||||
32E529092348A0C700EA46FF /* SDWebImageSwiftUIDemo-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImageSwiftUIDemo-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
32E5290B2348A0C700EA46FF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
@ -128,7 +124,6 @@
|
||||||
32E529512348A0DF00EA46FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
32E529512348A0DF00EA46FF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
32E529542348A0DF00EA46FF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
32E529542348A0DF00EA46FF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
32E529562348A0DF00EA46FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
32E529562348A0DF00EA46FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
32E7F120236CAAB8001688BC /* ActivityBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityBar.swift; sourceTree = "<group>"; };
|
|
||||||
3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
|
3E9F8B5F06960FFFBD1A5F99 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
|
||||||
473D7886C23B6FC5AFE35842 /* Pods_SDWebImageSwiftUIDemo_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageSwiftUIDemo_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
54859B427E0A79E823713963 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
|
||||||
|
@ -217,8 +212,7 @@
|
||||||
320CDC2D22FADB44007CF858 /* SceneDelegate.swift */,
|
320CDC2D22FADB44007CF858 /* SceneDelegate.swift */,
|
||||||
320CDC2F22FADB44007CF858 /* ContentView.swift */,
|
320CDC2F22FADB44007CF858 /* ContentView.swift */,
|
||||||
326B0D702345C01900D28269 /* DetailView.swift */,
|
326B0D702345C01900D28269 /* DetailView.swift */,
|
||||||
321A6BEF2345EC4E00B5BEFC /* ProgressBar.swift */,
|
3243598323E05C3D006DF9C5 /* Espera.swift */,
|
||||||
32E7F120236CAAB8001688BC /* ActivityBar.swift */,
|
|
||||||
320CDC3122FADB45007CF858 /* Assets.xcassets */,
|
320CDC3122FADB45007CF858 /* Assets.xcassets */,
|
||||||
320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */,
|
320CDC3622FADB45007CF858 /* LaunchScreen.storyboard */,
|
||||||
320CDC3922FADB45007CF858 /* Info.plist */,
|
320CDC3922FADB45007CF858 /* Info.plist */,
|
||||||
|
@ -798,8 +792,7 @@
|
||||||
320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */,
|
320CDC2C22FADB44007CF858 /* AppDelegate.swift in Sources */,
|
||||||
326B0D712345C01900D28269 /* DetailView.swift in Sources */,
|
326B0D712345C01900D28269 /* DetailView.swift in Sources */,
|
||||||
320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */,
|
320CDC2E22FADB44007CF858 /* SceneDelegate.swift in Sources */,
|
||||||
321A6BF02345EC4E00B5BEFC /* ProgressBar.swift in Sources */,
|
3243598423E05C3D006DF9C5 /* Espera.swift in Sources */,
|
||||||
32E7F121236CAAB8001688BC /* ActivityBar.swift in Sources */,
|
|
||||||
320CDC3022FADB44007CF858 /* ContentView.swift in Sources */,
|
320CDC3022FADB44007CF858 /* ContentView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -810,9 +803,8 @@
|
||||||
files = (
|
files = (
|
||||||
32E529622348A10B00EA46FF /* ContentView.swift in Sources */,
|
32E529622348A10B00EA46FF /* ContentView.swift in Sources */,
|
||||||
32E529632348A10B00EA46FF /* DetailView.swift in Sources */,
|
32E529632348A10B00EA46FF /* DetailView.swift in Sources */,
|
||||||
32E529642348A10B00EA46FF /* ProgressBar.swift in Sources */,
|
|
||||||
32E7F122236CAAB8001688BC /* ActivityBar.swift in Sources */,
|
|
||||||
32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */,
|
32E5290C2348A0C700EA46FF /* AppDelegate.swift in Sources */,
|
||||||
|
3243598523E05C3D006DF9C5 /* Espera.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -822,9 +814,8 @@
|
||||||
files = (
|
files = (
|
||||||
32E529652348A10B00EA46FF /* ContentView.swift in Sources */,
|
32E529652348A10B00EA46FF /* ContentView.swift in Sources */,
|
||||||
32E529662348A10B00EA46FF /* DetailView.swift in Sources */,
|
32E529662348A10B00EA46FF /* DetailView.swift in Sources */,
|
||||||
32E529672348A10B00EA46FF /* ProgressBar.swift in Sources */,
|
|
||||||
32E7F123236CAAB8001688BC /* ActivityBar.swift in Sources */,
|
|
||||||
32E529232348A0D300EA46FF /* AppDelegate.swift in Sources */,
|
32E529232348A0D300EA46FF /* AppDelegate.swift in Sources */,
|
||||||
|
3243598623E05C3D006DF9C5 /* Espera.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -835,8 +826,7 @@
|
||||||
32E5294E2348A0DE00EA46FF /* HostingController.swift in Sources */,
|
32E5294E2348A0DE00EA46FF /* HostingController.swift in Sources */,
|
||||||
32E529692348A10C00EA46FF /* DetailView.swift in Sources */,
|
32E529692348A10C00EA46FF /* DetailView.swift in Sources */,
|
||||||
32E529502348A0DE00EA46FF /* ExtensionDelegate.swift in Sources */,
|
32E529502348A0DE00EA46FF /* ExtensionDelegate.swift in Sources */,
|
||||||
32E5296A2348A10C00EA46FF /* ProgressBar.swift in Sources */,
|
3243598723E05C3D006DF9C5 /* Espera.swift in Sources */,
|
||||||
32E7F124236CAAB8001688BC /* ActivityBar.swift in Sources */,
|
|
||||||
32E529682348A10C00EA46FF /* ContentView.swift in Sources */,
|
32E529682348A10C00EA46FF /* ContentView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the SDWebImage package.
|
|
||||||
* (c) DreamPiggy <lizhuoli1126@126.com>
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ import SwiftUI
|
||||||
import SDWebImage
|
import SDWebImage
|
||||||
import SDWebImageSwiftUI
|
import SDWebImageSwiftUI
|
||||||
|
|
||||||
|
// Allows `String` in `ForEach`
|
||||||
extension String : Identifiable {
|
extension String : Identifiable {
|
||||||
public typealias ID = Int
|
public typealias ID = Int
|
||||||
public var 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 {
|
struct ContentView: View {
|
||||||
@State var imageURLs = [
|
@State var imageURLs = [
|
||||||
"http://assets.sbnation.com/assets/2512203/dogflops.gif",
|
"http://assets.sbnation.com/assets/2512203/dogflops.gif",
|
||||||
|
@ -107,18 +129,13 @@ struct ContentView: View {
|
||||||
#else
|
#else
|
||||||
WebImage(url: URL(string:url), isAnimating: self.$animated)
|
WebImage(url: URL(string:url), isAnimating: self.$animated)
|
||||||
.resizable()
|
.resizable()
|
||||||
.indicator { _, _ in
|
.indicator(.activity)
|
||||||
ActivityBar()
|
|
||||||
.foregroundColor(Color.white)
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
}
|
|
||||||
.animation(.easeInOut(duration: 0.5))
|
.animation(.easeInOut(duration: 0.5))
|
||||||
.transition(.fade)
|
.transition(.fade)
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if os(macOS) || os(iOS) || os(tvOS)
|
|
||||||
WebImage(url: URL(string:url))
|
WebImage(url: URL(string:url))
|
||||||
.resizable()
|
.resizable()
|
||||||
/**
|
/**
|
||||||
|
@ -131,19 +148,6 @@ struct ContentView: View {
|
||||||
.transition(.fade)
|
.transition(.fade)
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: CGFloat(100), height: CGFloat(100), alignment: .center)
|
.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)
|
Text((url as NSString).lastPathComponent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,29 +97,14 @@ struct DetailView: View {
|
||||||
#else
|
#else
|
||||||
WebImage(url: URL(string:url), options: [.progressiveLoad], isAnimating: $isAnimating)
|
WebImage(url: URL(string:url), options: [.progressiveLoad], isAnimating: $isAnimating)
|
||||||
.resizable()
|
.resizable()
|
||||||
.indicator { isAnimating, progress in
|
.indicator(.progress)
|
||||||
ProgressBar(value: progress)
|
|
||||||
.foregroundColor(.blue)
|
|
||||||
.frame(maxHeight: 6)
|
|
||||||
}
|
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if os(macOS) || os(iOS) || os(tvOS)
|
|
||||||
WebImage(url: URL(string:url), options: [.progressiveLoad])
|
WebImage(url: URL(string:url), options: [.progressiveLoad])
|
||||||
.resizable()
|
.resizable()
|
||||||
.indicator(.progress)
|
.indicator(.progress)
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
#else
|
|
||||||
WebImage(url: URL(string:url), options: [.progressiveLoad])
|
|
||||||
.resizable()
|
|
||||||
.indicator { isAnimating, progress in
|
|
||||||
ProgressBar(value: progress)
|
|
||||||
.foregroundColor(.blue)
|
|
||||||
.frame(maxHeight: 6)
|
|
||||||
}
|
|
||||||
.scaledToFit()
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of the SDWebImage package.
|
|
||||||
* (c) DreamPiggy <lizhuoli1126@126.com>
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -404,6 +404,7 @@ Which means, this project is one core use case and downstream dependency, which
|
||||||
- [libwebp](https://github.com/SDWebImage/libwebp-Xcode)
|
- [libwebp](https://github.com/SDWebImage/libwebp-Xcode)
|
||||||
- [Kingfisher](https://github.com/onevcat/Kingfisher)
|
- [Kingfisher](https://github.com/onevcat/Kingfisher)
|
||||||
- [SwiftUIX](https://github.com/SwiftUIX/SwiftUIX)
|
- [SwiftUIX](https://github.com/SwiftUIX/SwiftUIX)
|
||||||
|
- [Espera](https://github.com/JagCesar/Espera)
|
||||||
- [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect)
|
- [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect)
|
||||||
- [ViewInspector](https://github.com/nalexn/ViewInspector)
|
- [ViewInspector](https://github.com/nalexn/ViewInspector)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue