Skip to content

Commit 790781c

Browse files
uson1xmichaelhenry
authored andcommitted
Add ImageLoader protocol for handling loading of remote images
This commit adds new protocol - ImageLoader and simple implementation via Data(contentsOf: url). This simple implementation removes the requirement to include heavy SDWebImage dependency, but still makes it possible. Additionally, this commit makes it possible for clients to specify their own implementation of ImageLoader at call site. Commit also removes non-existing RightNavItemDelegate.swift file reference. And adds ImageViewerTransitionPresentationManager.swift, which was omitted.
1 parent 54069da commit 790781c

File tree

6 files changed

+95
-42
lines changed

6 files changed

+95
-42
lines changed

ImageViewer.swift.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
3FBC5E8F24E5910E00E68938 /* UINavigationBar_Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8324E5910E00E68938 /* UINavigationBar_Extensions.swift */; };
1414
3FBC5E9024E5910E00E68938 /* ImageCarouselViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8424E5910E00E68938 /* ImageCarouselViewController.swift */; };
1515
3FBC5E9124E5910E00E68938 /* ImageViewerOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8524E5910E00E68938 /* ImageViewerOption.swift */; };
16-
3FBC5E9224E5910E00E68938 /* RightNavItemDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8624E5910E00E68938 /* RightNavItemDelegate.swift */; };
1716
3FBC5E9324E5910E00E68938 /* ImageItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8724E5910E00E68938 /* ImageItem.swift */; };
1817
3FBC5E9424E5910E00E68938 /* ImageViewerTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8824E5910E00E68938 /* ImageViewerTheme.swift */; };
1918
3FBC5E9524E5910E00E68938 /* ImageViewerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8924E5910E00E68938 /* ImageViewerController.swift */; };
2019
3FBC5E9624E5910E00E68938 /* SimpleImageDatasource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FBC5E8A24E5910E00E68938 /* SimpleImageDatasource.swift */; };
2120
3FBC5E9D24E595AB00E68938 /* SDWebImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FBC5E9C24E595AB00E68938 /* SDWebImage.framework */; };
21+
CB0EEA2E261612E400E9B9CB /* ImageViewerTransitionPresentationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0EEA2D261612E400E9B9CB /* ImageViewerTransitionPresentationManager.swift */; };
2222
/* End PBXBuildFile section */
2323

2424
/* Begin PBXFileReference section */
@@ -29,13 +29,13 @@
2929
3FBC5E8324E5910E00E68938 /* UINavigationBar_Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationBar_Extensions.swift; sourceTree = "<group>"; };
3030
3FBC5E8424E5910E00E68938 /* ImageCarouselViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCarouselViewController.swift; sourceTree = "<group>"; };
3131
3FBC5E8524E5910E00E68938 /* ImageViewerOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewerOption.swift; sourceTree = "<group>"; };
32-
3FBC5E8624E5910E00E68938 /* RightNavItemDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightNavItemDelegate.swift; sourceTree = "<group>"; };
3332
3FBC5E8724E5910E00E68938 /* ImageItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageItem.swift; sourceTree = "<group>"; };
3433
3FBC5E8824E5910E00E68938 /* ImageViewerTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewerTheme.swift; sourceTree = "<group>"; };
3534
3FBC5E8924E5910E00E68938 /* ImageViewerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewerController.swift; sourceTree = "<group>"; };
3635
3FBC5E8A24E5910E00E68938 /* SimpleImageDatasource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleImageDatasource.swift; sourceTree = "<group>"; };
3736
3FBC5E9924E591CB00E68938 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3837
3FBC5E9C24E595AB00E68938 /* SDWebImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDWebImage.framework; path = Carthage/Build/iOS/SDWebImage.framework; sourceTree = SOURCE_ROOT; };
38+
CB0EEA2D261612E400E9B9CB /* ImageViewerTransitionPresentationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewerTransitionPresentationManager.swift; sourceTree = "<group>"; };
3939
/* End PBXFileReference section */
4040

4141
/* Begin PBXFrameworksBuildPhase section */
@@ -77,11 +77,11 @@
7777
3FBC5E8324E5910E00E68938 /* UINavigationBar_Extensions.swift */,
7878
3FBC5E8424E5910E00E68938 /* ImageCarouselViewController.swift */,
7979
3FBC5E8524E5910E00E68938 /* ImageViewerOption.swift */,
80-
3FBC5E8624E5910E00E68938 /* RightNavItemDelegate.swift */,
8180
3FBC5E8724E5910E00E68938 /* ImageItem.swift */,
8281
3FBC5E8824E5910E00E68938 /* ImageViewerTheme.swift */,
8382
3FBC5E8924E5910E00E68938 /* ImageViewerController.swift */,
8483
3FBC5E8A24E5910E00E68938 /* SimpleImageDatasource.swift */,
84+
CB0EEA2D261612E400E9B9CB /* ImageViewerTransitionPresentationManager.swift */,
8585
);
8686
path = Sources;
8787
sourceTree = "<group>";
@@ -206,12 +206,12 @@
206206
files = (
207207
3FBC5E9024E5910E00E68938 /* ImageCarouselViewController.swift in Sources */,
208208
3FBC5E9424E5910E00E68938 /* ImageViewerTheme.swift in Sources */,
209+
CB0EEA2E261612E400E9B9CB /* ImageViewerTransitionPresentationManager.swift in Sources */,
209210
3FBC5E9524E5910E00E68938 /* ImageViewerController.swift in Sources */,
210211
3FBC5E8E24E5910E00E68938 /* UIView_Extensions.swift in Sources */,
211212
3FBC5E9624E5910E00E68938 /* SimpleImageDatasource.swift in Sources */,
212213
3FBC5E8C24E5910E00E68938 /* UIImageView_Extensions.swift in Sources */,
213214
3FBC5E9124E5910E00E68938 /* ImageViewerOption.swift in Sources */,
214-
3FBC5E9224E5910E00E68938 /* RightNavItemDelegate.swift in Sources */,
215215
3FBC5E9324E5910E00E68938 /* ImageItem.swift in Sources */,
216216
3FBC5E8F24E5910E00E68938 /* UINavigationBar_Extensions.swift in Sources */,
217217
);

Sources/ImageCarouselViewController.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class ImageCarouselViewController:UIPageViewController, ImageViewerTransi
2323
}
2424

2525
weak var imageDatasource:ImageDataSource?
26+
let imageLoader:ImageLoader
2627

2728
var initialIndex = 0
2829

@@ -59,13 +60,15 @@ public class ImageCarouselViewController:UIPageViewController, ImageViewerTransi
5960
public init(
6061
sourceView:UIImageView,
6162
imageDataSource: ImageDataSource?,
63+
imageLoader: ImageLoader,
6264
options:[ImageViewerOption] = [],
6365
initialIndex:Int = 0) {
6466

6567
self.initialSourceView = sourceView
6668
self.initialIndex = initialIndex
6769
self.options = options
6870
self.imageDatasource = imageDataSource
71+
self.imageLoader = imageLoader
6972
let pageOptions = [UIPageViewController.OptionsKey.interPageSpacing: 20]
7073
super.init(
7174
transitionStyle: .scroll,
@@ -141,7 +144,8 @@ public class ImageCarouselViewController:UIPageViewController, ImageViewerTransi
141144
if let imageDatasource = imageDatasource {
142145
let initialVC:ImageViewerController = .init(
143146
index: initialIndex,
144-
imageItem: imageDatasource.imageItem(at: initialIndex))
147+
imageItem: imageDatasource.imageItem(at: initialIndex),
148+
imageLoader: imageLoader)
145149
setViewControllers([initialVC], direction: .forward, animated: true)
146150
}
147151
}
@@ -192,7 +196,8 @@ extension ImageCarouselViewController:UIPageViewControllerDataSource {
192196
let newIndex = vc.index - 1
193197
return ImageViewerController.init(
194198
index: newIndex,
195-
imageItem: imageDatasource.imageItem(at: newIndex))
199+
imageItem: imageDatasource.imageItem(at: newIndex),
200+
imageLoader: vc.imageLoader)
196201
}
197202

198203
public func pageViewController(
@@ -206,6 +211,7 @@ extension ImageCarouselViewController:UIPageViewControllerDataSource {
206211
let newIndex = vc.index + 1
207212
return ImageViewerController.init(
208213
index: newIndex,
209-
imageItem: imageDatasource.imageItem(at: newIndex))
214+
imageItem: imageDatasource.imageItem(at: newIndex),
215+
imageLoader: vc.imageLoader)
210216
}
211217
}

Sources/ImageItem.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,5 @@ import UIKit
22

33
public enum ImageItem {
44
case image(UIImage?)
5-
#if canImport(SDWebImage)
65
case url(URL, placeholder: UIImage?)
7-
#endif
86
}

Sources/ImageLoader.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Foundation
2+
#if canImport(SDWebImage)
3+
import SDWebImage
4+
#endif
5+
6+
public protocol ImageLoader {
7+
func loadImage(_ url: URL, placeholder: UIImage?, imageView: UIImageView, completion: @escaping (_ image: UIImage?) -> Void)
8+
}
9+
10+
public struct URLSessionImageLoader: ImageLoader {
11+
public init() {}
12+
13+
public func loadImage(_ url: URL, placeholder: UIImage?, imageView: UIImageView, completion: @escaping (UIImage?) -> Void) {
14+
if let placeholder = placeholder {
15+
imageView.image = placeholder
16+
}
17+
18+
DispatchQueue.global(qos: .background).async {
19+
guard let data = try? Data(contentsOf: url), let image = UIImage(data: data) else {
20+
completion(nil)
21+
return
22+
}
23+
24+
DispatchQueue.main.async {
25+
imageView.image = image
26+
}
27+
}
28+
}
29+
}
30+
31+
#if canImport(SDWebImage)
32+
struct SDWebImageLoader: ImageLoader {
33+
func loadImage(_ url: URL, placeholder: UIImage?, imageView: UIImageView, completion: @escaping (UIImage?) -> Void) {
34+
imageView.sd_setImage(
35+
with: url,
36+
placeholderImage: placeholder,
37+
options: [],
38+
progress: nil) {(img, err, type, url) in
39+
DispatchQueue.main.async {
40+
completion(img)
41+
}
42+
}
43+
}
44+
}
45+
#endif

Sources/ImageViewerController.swift

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import UIKit
2-
#if canImport(SDWebImage)
3-
import SDWebImage
4-
#endif
52

63
class ImageViewerController:UIViewController,
74
UIGestureRecognizerDelegate {
85

96
var imageView: UIImageView = UIImageView(frame: .zero)
10-
7+
let imageLoader: ImageLoader
8+
119
var backgroundView:UIView? {
1210
guard let _parent = parent as? ImageCarouselViewController
1311
else { return nil}
@@ -37,10 +35,12 @@ UIGestureRecognizerDelegate {
3735

3836
init(
3937
index: Int,
40-
imageItem:ImageItem) {
41-
38+
imageItem:ImageItem,
39+
imageLoader: ImageLoader) {
40+
4241
self.index = index
4342
self.imageItem = imageItem
43+
self.imageLoader = imageLoader
4444
super.init(nibName: nil, bundle: nil)
4545
}
4646

@@ -88,18 +88,12 @@ UIGestureRecognizerDelegate {
8888
case .image(let img):
8989
imageView.image = img
9090
imageView.layoutIfNeeded()
91-
#if canImport(SDWebImage)
9291
case .url(let url, let placeholder):
93-
imageView.sd_setImage(
94-
with: url,
95-
placeholderImage: placeholder,
96-
options: [],
97-
progress: nil) {(img, err, type, url) in
98-
DispatchQueue.main.async {[weak self] in
99-
self?.layout()
100-
}
92+
imageLoader.loadImage(url, placeholder: placeholder, imageView: imageView) { (image) in
93+
DispatchQueue.main.async {[weak self] in
94+
self?.layout()
95+
}
10196
}
102-
#endif
10397
default:
10498
break
10599
}

Sources/UIImageView_Extensions.swift

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extension UIImageView {
66
private class TapWithDataRecognizer:UITapGestureRecognizer {
77
weak var from:UIViewController?
88
var imageDatasource:ImageDataSource?
9+
var imageLoader:ImageLoader?
910
var initialIndex:Int = 0
1011
var options:[ImageViewerOption] = []
1112
}
@@ -18,20 +19,22 @@ extension UIImageView {
1819

1920
public func setupImageViewer(
2021
options:[ImageViewerOption] = [],
21-
from:UIViewController? = nil) {
22+
from:UIViewController? = nil,
23+
imageLoader:ImageLoader? = nil) {
2224
setup(
2325
datasource: SimpleImageDatasource(imageItems: [.image(image)]),
2426
options: options,
25-
from: from)
27+
from: from,
28+
imageLoader: imageLoader)
2629
}
27-
28-
#if canImport(SDWebImage)
30+
2931
public func setupImageViewer(
3032
url:URL,
3133
initialIndex:Int = 0,
3234
placeholder: UIImage? = nil,
3335
options:[ImageViewerOption] = [],
34-
from:UIViewController? = nil) {
36+
from:UIViewController? = nil,
37+
imageLoader:ImageLoader? = nil) {
3538

3639
let datasource = SimpleImageDatasource(
3740
imageItems: [url].compactMap {
@@ -41,15 +44,16 @@ extension UIImageView {
4144
datasource: datasource,
4245
initialIndex: initialIndex,
4346
options: options,
44-
from: from)
47+
from: from,
48+
imageLoader: imageLoader)
4549
}
46-
#endif
4750

4851
public func setupImageViewer(
4952
images:[UIImage],
5053
initialIndex:Int = 0,
5154
options:[ImageViewerOption] = [],
52-
from:UIViewController? = nil) {
55+
from:UIViewController? = nil,
56+
imageLoader:ImageLoader? = nil) {
5357

5458
let datasource = SimpleImageDatasource(
5559
imageItems: images.compactMap {
@@ -59,16 +63,17 @@ extension UIImageView {
5963
datasource: datasource,
6064
initialIndex: initialIndex,
6165
options: options,
62-
from: from)
66+
from: from,
67+
imageLoader: imageLoader)
6368
}
64-
65-
#if canImport(SDWebImage)
69+
6670
public func setupImageViewer(
6771
urls:[URL],
6872
initialIndex:Int = 0,
6973
options:[ImageViewerOption] = [],
7074
placeholder: UIImage? = nil,
71-
from:UIViewController? = nil) {
75+
from:UIViewController? = nil,
76+
imageLoader:ImageLoader? = nil) {
7277

7378
let datasource = SimpleImageDatasource(
7479
imageItems: urls.compactMap {
@@ -78,28 +83,31 @@ extension UIImageView {
7883
datasource: datasource,
7984
initialIndex: initialIndex,
8085
options: options,
81-
from: from)
86+
from: from,
87+
imageLoader: imageLoader)
8288
}
83-
#endif
8489

8590
public func setupImageViewer(
8691
datasource:ImageDataSource,
8792
initialIndex:Int = 0,
8893
options:[ImageViewerOption] = [],
89-
from:UIViewController? = nil) {
94+
from:UIViewController? = nil,
95+
imageLoader:ImageLoader? = nil) {
9096

9197
setup(
9298
datasource: datasource,
9399
initialIndex: initialIndex,
94100
options: options,
95-
from: from)
101+
from: from,
102+
imageLoader: imageLoader)
96103
}
97104

98105
private func setup(
99106
datasource:ImageDataSource?,
100107
initialIndex:Int = 0,
101108
options:[ImageViewerOption] = [],
102-
from: UIViewController? = nil) {
109+
from: UIViewController? = nil,
110+
imageLoader:ImageLoader? = nil) {
103111

104112
var _tapRecognizer:TapWithDataRecognizer?
105113
gestureRecognizers?.forEach {
@@ -121,6 +129,7 @@ extension UIImageView {
121129
}
122130
// Pass the Data
123131
_tapRecognizer!.imageDatasource = datasource
132+
_tapRecognizer!.imageLoader = imageLoader
124133
_tapRecognizer!.initialIndex = initialIndex
125134
_tapRecognizer!.options = options
126135
_tapRecognizer!.from = from
@@ -133,6 +142,7 @@ extension UIImageView {
133142
let imageCarousel = ImageCarouselViewController.init(
134143
sourceView: sourceView,
135144
imageDataSource: sender.imageDatasource,
145+
imageLoader: sender.imageLoader ?? URLSessionImageLoader(),
136146
options: sender.options,
137147
initialIndex: sender.initialIndex)
138148
let presentFromVC = sender.from ?? vc

0 commit comments

Comments
 (0)