-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// swift-tools-version:5.3 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "Laden", | ||
platforms: [ | ||
.iOS(.v13), | ||
.macOS(.v10_15), | ||
.watchOS(.v6) | ||
], | ||
products: [ | ||
.library( | ||
name: "Laden", | ||
targets: ["Laden"]), | ||
], | ||
targets: [ | ||
.target( | ||
name: "Laden", | ||
dependencies: []) | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<H1 align="center">Laden</H1> | ||
|
||
<p align="center">SwiftUI loading indicator view</p> | ||
<p align="center"> | ||
<img src="./Resources/loading.gif"/> | ||
</p> | ||
|
||
--- | ||
|
||
### Installation | ||
|
||
Since this component is built using Swift Package Manager, it is pretty straight forward to use: | ||
|
||
1. In Xcode (11+), open your project and navigate to File > Swift Packages > Add Package Dependency... | ||
2. Paste the repository URL (https://github.com/vinhnx/Laden) and click Next. | ||
3. For Rules, select Branch (with branch set to master). | ||
4. Click Finish to resolve package into your Xcode project. | ||
|
||
### Usage | ||
|
||
At simplest form: | ||
```swift | ||
import SwiftUI | ||
import Laden | ||
|
||
struct ContentView: View { | ||
var body: some View { | ||
Laden.CircleLoadingView() | ||
} | ||
} | ||
``` | ||
|
||
--- | ||
|
||
To show loading view on top on current view by embedding inside a `ZStack`: | ||
```swift | ||
ZStack { | ||
Text("Some text") // your content view | ||
Laden.CircleOutlineLoadingView() | ||
} | ||
``` | ||
|
||
 | ||
|
||
--- | ||
|
||
To indicate loading state, have a private loading bool `@State` and bind it to Laden's `isAnimating` initialzier: | ||
```swift | ||
import SwiftUI | ||
import Laden | ||
|
||
struct ContentView: View { | ||
@State private var isLoading = true | ||
|
||
var body: some View { | ||
VStack { | ||
Laden.CircleLoadingView(isAnimating: isLoading) | ||
Button(isLoading ? "Stop loading" : "Start loading") { | ||
self.isLoading.toggle() | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
--- | ||
|
||
To show or hide loading view, have a private show/hide bool `@State` and modify said loading with `.hidden` attribute, when toggled: | ||
```swift | ||
import SwiftUI | ||
import Laden | ||
|
||
struct ContentView: View { | ||
@State private var shouldLoadingView = true | ||
|
||
private var loadingView = SwiftUILoading.CircleOutlineLoadingView() | ||
|
||
var body: some View { | ||
VStack { | ||
if shouldLoadingView { | ||
loadingView | ||
.hidden() | ||
} else { | ||
loadingView | ||
} | ||
|
||
Button(shouldCircleView ? "Show" : "Hide") { | ||
self.shouldLoadingView.toggle() | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
--- | ||
|
||
To customize loading view color, use `color` initializer: | ||
|
||
```swift | ||
Laden.CircleOutlineLoadingView(color: .red) | ||
``` | ||
|
||
--- | ||
|
||
### Help, feedback or suggestions? | ||
|
||
Feel free to open issue or contact me on [Twitter](https://twitter.com/@vinhnx) for discussions, news & announcements & other projects. 🚀 | ||
|
||
I hope you like it! :) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
// | ||
// Created by Vĩnh Nguyễn on 12/15/20. | ||
// | ||
|
||
import SwiftUI | ||
|
||
/// Loading view protocol that define default configurations. | ||
public protocol LoadingAnimatable: View { | ||
/// Whether this loading view is animating. | ||
var isAnimating: Bool { get } | ||
|
||
/// Default color for loading view. | ||
var color: Color { get } | ||
|
||
/// The default size for loading view. | ||
var size: CGSize { get set } | ||
|
||
/// Default stroke line width for loading bar. | ||
var strokeLineWidth: CGFloat { get } | ||
} | ||
|
||
/// Loading view container, for namespacing. | ||
public enum Laden {} | ||
|
||
extension Laden { | ||
/// Circular loading view | ||
public struct CircleLoadingView: LoadingAnimatable { | ||
|
||
public var isAnimating: Bool = true | ||
public var color: Color = .green | ||
public var size: CGSize = CGSize(width: 30, height: 30) | ||
public var strokeLineWidth: CGFloat = 3 | ||
|
||
/// The left lobe fraction of the loading bar. | ||
public var trimEndFraction: CGFloat = 0.8 | ||
|
||
/// Current rotation value. | ||
@State private var rotation: Double = 0 | ||
|
||
/// Timer that will continously draw the animation for loading view. | ||
private let timer = Timer | ||
.publish(every: 0.1, on: .main, in: .common) | ||
.autoconnect() | ||
|
||
/// - Parameters: | ||
/// - isAnimating: Whether this loading view is animating | ||
/// - color: Default color for loading view. | ||
/// - size: The default size for loading view. | ||
/// - trimEndFraction: The left lobe fraction of the loading bar. | ||
/// - strokeLineWidth: Default stroke line width for loading bar. | ||
public init(isAnimating: Bool = true, color: Color = .green, size: CGSize = CGSize(width: 30, height: 30), trimEndFraction: CGFloat = 0.8, strokeLineWidth: CGFloat = 8) { | ||
self.isAnimating = isAnimating | ||
self.color = color | ||
self.size = size | ||
self.trimEndFraction = trimEndFraction | ||
self.strokeLineWidth = strokeLineWidth | ||
} | ||
|
||
public var body: some View { | ||
Circle() | ||
.trim(from: 0, to: trimEndFraction) | ||
.stroke(color, lineWidth: strokeLineWidth) | ||
.frame(width: size.width, height: size.height) | ||
.rotationEffect(.degrees(rotation)) | ||
.animation(isAnimating ? .linear : .none) | ||
.onReceive(timer) { _ in | ||
if isAnimating { rotation += 36 } | ||
} | ||
} | ||
} | ||
|
||
/// Circular loading view with outline background | ||
public struct CircleOutlineLoadingView: LoadingAnimatable { | ||
public var isAnimating: Bool = true | ||
public var color: Color = .green | ||
public var size: CGSize = CGSize(width: 30, height: 30) | ||
public var strokeLineWidth: CGFloat = 8 | ||
|
||
/// The left lobe fraction of the loading bar. | ||
public var trimEndFraction: CGFloat = 0.8 | ||
|
||
/// Outline bar color. | ||
public var outlineBarColor: Color = Color(.systemGray5) | ||
|
||
/// Current rotation value. | ||
@State private var rotation: Double = 0 | ||
|
||
/// Timer that will continously draw the animation for loading view. | ||
private let timer = Timer | ||
.publish(every: 0.1, on: .main, in: .common) | ||
.autoconnect() | ||
|
||
/// - Parameters: | ||
/// - isAnimating: Whether this loading view is animating | ||
/// - color: Default color for loading view. | ||
/// - size: The default size for loading view. | ||
/// - trimEndFraction: The left lobe fraction of the loading bar. | ||
/// - strokeLineWidth: Default stroke line width for loading bar. | ||
public init(isAnimating: Bool = true, color: Color = .green, size: CGSize = CGSize(width: 30, height: 30), strokeLineWidth: CGFloat = 8, trimEndFraction: CGFloat = 0.8) { | ||
self.isAnimating = isAnimating | ||
self.color = color | ||
self.size = size | ||
self.strokeLineWidth = strokeLineWidth | ||
self.trimEndFraction = trimEndFraction | ||
} | ||
|
||
public var body: some View { | ||
ZStack { | ||
Circle() | ||
.stroke(outlineBarColor, lineWidth: strokeLineWidth) | ||
.frame(width: size.width, height: size.height) | ||
|
||
Circle() | ||
.trim(from: 0, to: trimEndFraction) | ||
.stroke(color, lineWidth: strokeLineWidth / 2) | ||
.frame(width: size.width, height: size.height) | ||
.rotationEffect(.degrees(rotation)) | ||
.animation(isAnimating ? .linear : .none) | ||
.onReceive(timer) { _ in | ||
if isAnimating { rotation += 36 } | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Reversible loading bar view. | ||
public struct BarLoadingView: LoadingAnimatable { | ||
public var isAnimating: Bool = true | ||
public var color: Color = .green | ||
public var size: CGSize = CGSize(width: 200, height: 30) | ||
public var strokeLineWidth: CGFloat = 3 | ||
|
||
/// Outline bar color. | ||
public var outlineBarColor: Color = Color(.systemGray5) | ||
|
||
/// Current indicator view's x offset. | ||
@State private var offset: CGFloat = 0 | ||
|
||
/// The caculated width of indicator view. | ||
private var indicatorWidth: CGFloat { | ||
size.width * 0.3 | ||
} | ||
|
||
/// Timer that will continously draw the animation for loading view. | ||
private let timer = Timer | ||
.publish(every: 1, on: .main, in: .common) | ||
.autoconnect() | ||
|
||
/// - Parameters: | ||
/// - isAnimating: Whether this loading view is animating | ||
/// - color: Default color for loading view. | ||
/// - size: The default size for loading view. | ||
/// - strokeLineWidth: Default stroke line width for loading bar. | ||
public init(isAnimating: Bool = true, color: Color = .green, size: CGSize = CGSize(width: 200, height: 30), strokeLineWidth: CGFloat = 3) { | ||
self.isAnimating = isAnimating | ||
self.color = color | ||
self.size = size | ||
self.strokeLineWidth = strokeLineWidth | ||
} | ||
|
||
public var body: some View { | ||
ZStack(alignment: Alignment(horizontal: .leading, vertical: .center)) { | ||
RoundedRectangle(cornerRadius: strokeLineWidth) | ||
.stroke(outlineBarColor, lineWidth: strokeLineWidth) | ||
.frame(width: size.width, height: strokeLineWidth) | ||
|
||
RoundedRectangle(cornerRadius: strokeLineWidth) | ||
.stroke(color, lineWidth: strokeLineWidth) | ||
.frame(width: indicatorWidth, height: strokeLineWidth) | ||
.offset(x: offset, y: 0) | ||
.animation(Animation.easeInOut(duration: 0.5).repeatForever(autoreverses: true)) | ||
.onReceive(timer) { _ in | ||
if isAnimating { offset = size.width - indicatorWidth } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct Previews: PreviewProvider { | ||
static var previews: some View { | ||
VStack(spacing: 50) { | ||
Laden.BarLoadingView(color: .red) | ||
Laden.CircleLoadingView(color: .green) | ||
Laden.CircleOutlineLoadingView(color: .orange) | ||
} | ||
} | ||
} |