├── .gitignore
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ ├── ali.xcuserdatad
│ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── luca.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── Package.swift
├── README.md
├── Sources
└── ImageWithActivityIndicator
│ ├── ActivityIndicator.swift
│ ├── ImageWithActivityIndicator.swift
│ ├── ViewLoader.swift
│ └── ViewLoaders.swift
└── Tests
├── ImageWithActivityIndicatorTests
├── ImageWithActivityIndicatorTests.swift
└── XCTestManifests.swift
└── LinuxMain.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcuserdata/ali.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | ImageWithActivityIndicator.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | ImageWithActivityIndicator
16 |
17 | primary
18 |
19 |
20 | ImageWithActivityIndicatorTests
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcuserdata/luca.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | ImageWithActivityIndicator.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "ImageWithActivityIndicator",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "ImageWithActivityIndicator",
12 | targets: ["ImageWithActivityIndicator"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "ImageWithActivityIndicator",
23 | dependencies: []),
24 | .testTarget(
25 | name: "ImageWithActivityIndicatorTests",
26 | dependencies: ["ImageWithActivityIndicator"]),
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ViewWithActivityIndicator
2 |
3 |
4 |
5 | `ViewWithActivityIndicator` is a SwiftUI view that download and display image from URL and displaying Activity Indicator while loading .
6 |
7 | # ScreenShots
8 |
9 | 
10 |
11 | Demo app [ViewWithActivityIndicatorDemo](https://github.com/blackwiz4rd/ViewWithActivityIndicatorDemo).
12 |
13 | ## Installation
14 |
15 | `ViewWithActivityIndicator` is a Swift Package and you can install it with Xcode 11:
16 | - Copy SSH `git@github.com:blackwiz4rd/ViewWithActivityIndicator.git` or HTTPS `https://github.com/blackwiz4rd/ViewWithActivityIndicator.git` URL from github;
17 | - Open **File/Swift Packages/Add Package Dependency...** in Xcode 11;
18 | - Paste the URL and follow steps.
19 |
20 | ## Usage
21 |
22 | `ViewWithActivityIndicator` must be initialized with a URL and optional placeholder image.
23 |
24 | ```swift
25 | let url = ""
26 |
27 | ViewWithActivityIndicator(imageURL: url)
28 |
29 | ViewWithActivityIndicator(imageURL: url,placeHolder: "icon")
30 | ```
31 |
32 | Using in a view:
33 |
34 | ```swift
35 | import SwiftUI
36 | import ViewWithActivityIndicator
37 |
38 | struct ContentView : View {
39 |
40 |
41 | let loader: ViewLoader = ViewLoader(url: "https://picsum.photos/300")
42 |
43 | var body: some View {
44 | ViewWithActivityIndicator(placeHolder: "", showActivityIndicator: true, viewLoader: loader) {
45 | Image(uiImage: UIImage(data:self.loader.getData()) ?? UIImage())
46 | }
47 | }
48 | }
49 | ```
50 |
51 | Using in a list:
52 |
53 | ```swift
54 | import SwiftUI
55 | import ViewWithActivityIndicator
56 |
57 | struct ContentView : View {
58 | let urls: [String]
59 | let loader: ViewLoader = ViewLoader(url: "https://picsum.photos/300")
60 |
61 | var body: some View {
62 | List(urls, id: \.self) { url in
63 | HStack {
64 | ViewWithActivityIndicator(imageURL: url)
65 | .frame(width: 100.0, height: 100.0)
66 | Text("\(url)")
67 | }
68 | }
69 | }
70 | }
71 | ```
72 |
73 | ViewLoaders allows to create multiple loaders given a String array:
74 | ```swift
75 | @available(iOS 13.0, *)
76 | public struct ViewLoaders {
77 | var loaders: [ViewLoader] = []
78 | init(urls: [String]) {
79 | for url in urls {
80 | loaders.append(ViewLoader(url: url))
81 | }
82 | }
83 | }
84 | ```
85 |
--------------------------------------------------------------------------------
/Sources/ImageWithActivityIndicator/ActivityIndicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActivityIndicator.swift
3 | // testswiftui
4 | //
5 | // Created by Ali Adam on 6/12/19.
6 | // Copyright © 2019 AliAdam. All rights reserved.
7 | //
8 | import SwiftUI
9 |
10 | @available(iOS 13.0, *)
11 | public struct ActivityIndicator: UIViewRepresentable {
12 |
13 | let style: UIActivityIndicatorView.Style
14 |
15 | public func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView {
16 | return UIActivityIndicatorView(style: style)
17 | }
18 |
19 | public func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) {
20 | uiView.startAnimating()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/ImageWithActivityIndicator/ImageWithActivityIndicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIView.swift
3 | // testswiftui
4 | //
5 | // Created by Ali Adam on 6/12/19.
6 | // Copyright © 2019 AliAdam. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 |
12 | @available(iOS 13.0, *)
13 | public struct ViewWithActivityIndicator : View {
14 |
15 | private let style: UIActivityIndicatorView.Style = .medium
16 |
17 | @ObservedObject private var viewLoader:ViewLoader
18 | private var content: () -> Content
19 | private let placeHolder:String
20 | private let showActivityIndicator:Bool
21 |
22 | public init(placeHolder: String = "",showActivityIndicator:Bool = true, viewLoader:ViewLoader, @ViewBuilder _ content: @escaping () -> Content){
23 | self.placeHolder = placeHolder
24 | self.showActivityIndicator = showActivityIndicator
25 | self.viewLoader = viewLoader
26 | self.content = content
27 | }
28 |
29 | public var body: some View {
30 | ZStack(){
31 | if (viewLoader.data.isEmpty) {
32 | if (placeHolder != "") {
33 | Image(placeHolder)
34 | .resizable()
35 | .scaledToFit()
36 | }
37 |
38 | if showActivityIndicator {
39 | ActivityIndicator(style: .large)
40 | }
41 | }
42 | else{
43 | content()
44 | }
45 | }
46 | .onAppear(perform: loadImage)
47 | }
48 |
49 | private func loadImage() {
50 | self.viewLoader.loadData()
51 | }
52 |
53 | }
54 |
55 | #if DEBUG
56 |
57 | struct ImageWithActivityIndicator_Previews: PreviewProvider {
58 | @available(iOS 13.0, *)
59 | static var previews: some View {
60 | Text("not used")
61 | }
62 | }
63 | #endif
64 |
--------------------------------------------------------------------------------
/Sources/ImageWithActivityIndicator/ViewLoader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageLoader.swift
3 | // testswiftui
4 | //
5 | // Created by Ali Adam on 6/12/19.
6 | // Copyright © 2019 AliAdam. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | @available(iOS 13.0, *)
12 | public class ViewLoader: ObservableObject {
13 |
14 | @Published var data = Data()
15 | let url:String
16 |
17 | public init(url:String){
18 | self.url = url
19 | }
20 |
21 | public func loadData() {
22 | guard let url = URL(string:url) else {
23 | return
24 | }
25 |
26 | URLSession.shared.dataTask(with: url){(data,_,_) in
27 | guard let data = data else {return}
28 | DispatchQueue.main.async {
29 | self.data = data
30 | }
31 | }.resume()
32 | }
33 |
34 | public func getData() -> Data {
35 | return data
36 | }
37 |
38 | public func getUrl() -> String {
39 | return url
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/ImageWithActivityIndicator/ViewLoaders.swift:
--------------------------------------------------------------------------------
1 | //
2 | // File.swift
3 | //
4 | //
5 | // Created by luca on 9/5/19.
6 | //
7 |
8 | @available(iOS 13.0, *)
9 | public struct ViewLoaders {
10 | var loaders: [ViewLoader] = []
11 | init(urls: [String]) {
12 | for url in urls {
13 | loaders.append(ViewLoader(url: url))
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Tests/ImageWithActivityIndicatorTests/ImageWithActivityIndicatorTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import ImageWithActivityIndicator
3 |
4 | final class ImageWithActivityIndicatorTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | // XCTAssertEqual(ImageWithActivityIndicator().text, "Hello, World!")
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/ImageWithActivityIndicatorTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(ImageWithActivityIndicatorTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import ImageWithActivityIndicatorTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += ImageWithActivityIndicatorTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------