Skip to content

Commit 7d8e10d

Browse files
committed
MMMLoadableImageView: separate 'syncing' and 'failed' placeholders
1 parent 6f84dcf commit 7d8e10d

File tree

2 files changed

+33
-14
lines changed

2 files changed

+33
-14
lines changed

MMMCommonUI.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Pod::Spec.new do |s|
77

88
s.name = "MMMCommonUI"
9-
s.version = "3.12.2"
9+
s.version = "3.13.0"
1010
s.summary = "Small UI-related pieces reused in many components from MMMTemple"
1111
s.description = "#{s.summary}."
1212
s.homepage = "https://github.com/mediamonks/#{s.name}"

Sources/MMMCommonUI/MMMLoadableImageView.swift

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ public final class MMMLoadableImageView: NonStoryboardableView {
3434
/// Note that in case of the `.fill` mode the bounds of this view might reside outside of the receiver's bounds.
3535
public var alignmentView: UIView { imageView }
3636

37-
// No reason to open this for changes at any time.
38-
private let placeholderImage: UIImage?
39-
4037
private var imageObserver: MMMLoadableObserver?
4138

4239
/// A loadable image this image view should display and track. Just set and forget.
@@ -58,13 +55,30 @@ public final class MMMLoadableImageView: NonStoryboardableView {
5855
}
5956
}
6057

61-
// MARK: Init
62-
63-
// TODO: visually distinguish between 'loading' and 'failed to load' states, e.g. by using two placeholders or possibly using "shimmer"-kind animation.
64-
// TODO: this is where effects like shadows and corners can be added as well.
65-
public init(placeholderImage: UIImage? = nil, mode: Mode = .fit) {
58+
/// A version of the initializer allowing one placeholder for both cases for compatibility
59+
/// with the existing code.
60+
///
61+
/// - Parameters:
62+
/// - placeholderImage: An image to use while `image` has no contents or is not set.
63+
public convenience init(placeholderImage: UIImage? = nil, mode: Mode = .fit) {
64+
self.init(syncingPlaceholder: placeholderImage, failedPlaceholder: placeholderImage, mode: mode)
65+
}
6666

67-
self.placeholderImage = placeholderImage
67+
private let syncingPlaceholder: UIImage?
68+
private let failedPlaceholder: UIImage?
69+
70+
/// - Parameters:
71+
/// - syncingPlaceholder: A placeholder to use when `image` has no contents (or is not set)
72+
/// and is either `.idle` or `.syncing`.
73+
/// - failedPlaceholder: An placeholder to use when `image` has failed loading.
74+
public init(
75+
syncingPlaceholder: UIImage? = nil,
76+
failedPlaceholder: UIImage? = nil,
77+
mode: Mode = .fit
78+
) {
79+
80+
self.syncingPlaceholder = syncingPlaceholder
81+
self.failedPlaceholder = failedPlaceholder
6882

6983
super.init()
7084

@@ -123,16 +137,21 @@ public final class MMMLoadableImageView: NonStoryboardableView {
123137
private func update(animated: Bool) {
124138

125139
guard let loadableImage = image else {
126-
imageView.image = placeholderImage
140+
imageView.image = syncingPlaceholder
127141
return
128142
}
129143

130144
if loadableImage.isContentsAvailable {
131145
updateImage(with: loadableImage.image, animated: animated)
132146
} else {
133-
assert(loadableImage.loadableState == .syncing || loadableImage.loadableState == .didFailToSync)
134-
// TODO: currently we don't distinguish between loading and failed here, but would be better to so.
135-
updateImage(with: placeholderImage)
147+
switch loadableImage.loadableState {
148+
case .idle, .syncing:
149+
updateImage(with: syncingPlaceholder)
150+
case .didFailToSync, .didSyncSuccessfully:
151+
updateImage(with: failedPlaceholder)
152+
// We expect `isContentsAvailable` to be true in this case.
153+
assert(loadableImage.loadableState != .didSyncSuccessfully)
154+
}
136155
}
137156
}
138157

0 commit comments

Comments
 (0)