react-native-fast-image 8.6.41 → 8.6.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#import "FFFastImageView.h"
|
|
2
2
|
#import <SDWebImage/UIImage+MultiFormat.h>
|
|
3
3
|
#import <SDWebImage/UIView+WebCache.h>
|
|
4
|
+
#import <React/RCTUtils.h>
|
|
4
5
|
|
|
5
6
|
@interface FFFastImageView ()
|
|
6
7
|
|
|
@@ -14,11 +15,31 @@
|
|
|
14
15
|
// The image before tinting, kept while a tint is applied so the tint can be
|
|
15
16
|
// changed or removed. nil when there's no tint (super.image is untinted).
|
|
16
17
|
@property(nonatomic, strong) UIImage* untintedImage;
|
|
18
|
+
// Whether the current load was already restarted after the app came back
|
|
19
|
+
// from the background (see downloadImage:).
|
|
20
|
+
@property(nonatomic, assign) BOOL retriedAfterBackground;
|
|
21
|
+
// Waits for the app to be active again, to restart a load.
|
|
22
|
+
@property(nonatomic, strong) id activeObserver;
|
|
17
23
|
|
|
18
24
|
@end
|
|
19
25
|
|
|
26
|
+
// When the app last went to the background (CACurrentMediaTime), or 0.
|
|
27
|
+
static CFTimeInterval FFFEnteredBackgroundAt = 0;
|
|
28
|
+
|
|
20
29
|
@implementation FFFastImageView
|
|
21
30
|
|
|
31
|
+
+ (void) initialize {
|
|
32
|
+
if (self != [FFFastImageView class]) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
[[NSNotificationCenter defaultCenter] addObserverForName: UIApplicationDidEnterBackgroundNotification
|
|
36
|
+
object: nil
|
|
37
|
+
queue: [NSOperationQueue mainQueue]
|
|
38
|
+
usingBlock: ^(NSNotification* notification) {
|
|
39
|
+
FFFEnteredBackgroundAt = CACurrentMediaTime();
|
|
40
|
+
}];
|
|
41
|
+
}
|
|
42
|
+
|
|
22
43
|
- (id) init {
|
|
23
44
|
self = [super init];
|
|
24
45
|
self.resizeMode = RCTResizeModeCover;
|
|
@@ -29,10 +50,34 @@
|
|
|
29
50
|
- (void) setResizeMode: (RCTResizeMode)resizeMode {
|
|
30
51
|
if (_resizeMode != resizeMode) {
|
|
31
52
|
_resizeMode = resizeMode;
|
|
32
|
-
self
|
|
53
|
+
[self updateContentMode];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The content mode for resizeMode. `center` scales an image larger than the
|
|
58
|
+
// view down to fit, as React Native's Image and Android do (#866); only a
|
|
59
|
+
// smaller one is shown at its own size. UIViewContentModeCenter alone showed
|
|
60
|
+
// large images at full size, cropped. So it depends on the image and the
|
|
61
|
+
// view's size.
|
|
62
|
+
- (void) updateContentMode {
|
|
63
|
+
UIViewContentMode contentMode = (UIViewContentMode) _resizeMode;
|
|
64
|
+
if (_resizeMode == RCTResizeModeCenter) {
|
|
65
|
+
CGSize imageSize = super.image.size;
|
|
66
|
+
CGSize viewSize = self.bounds.size;
|
|
67
|
+
if (imageSize.width > viewSize.width || imageSize.height > viewSize.height) {
|
|
68
|
+
contentMode = UIViewContentModeScaleAspectFit;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (self.contentMode != contentMode) {
|
|
72
|
+
self.contentMode = contentMode;
|
|
33
73
|
}
|
|
34
74
|
}
|
|
35
75
|
|
|
76
|
+
- (void) layoutSubviews {
|
|
77
|
+
[super layoutSubviews];
|
|
78
|
+
[self updateContentMode];
|
|
79
|
+
}
|
|
80
|
+
|
|
36
81
|
- (void) setOnFastImageLoadEnd: (RCTDirectEventBlock)onFastImageLoadEnd {
|
|
37
82
|
_onFastImageLoadEnd = onFastImageLoadEnd;
|
|
38
83
|
if (self.hasCompleted && _onFastImageLoadEnd) {
|
|
@@ -121,6 +166,7 @@
|
|
|
121
166
|
self.untintedImage = nil;
|
|
122
167
|
super.image = image;
|
|
123
168
|
}
|
|
169
|
+
[self updateContentMode];
|
|
124
170
|
}
|
|
125
171
|
|
|
126
172
|
- (void) sendOnLoad: (UIImage*)image {
|
|
@@ -232,6 +278,8 @@
|
|
|
232
278
|
}
|
|
233
279
|
self.hasCompleted = NO;
|
|
234
280
|
self.hasErrored = NO;
|
|
281
|
+
self.retriedAfterBackground = NO;
|
|
282
|
+
[self stopWaitingForActive];
|
|
235
283
|
|
|
236
284
|
[self downloadImage: _source options: options context: context];
|
|
237
285
|
} else if (_defaultSource) {
|
|
@@ -267,6 +315,7 @@
|
|
|
267
315
|
});
|
|
268
316
|
};
|
|
269
317
|
}
|
|
318
|
+
CFTimeInterval startedAt = CACurrentMediaTime();
|
|
270
319
|
[self sd_setImageWithURL: _source.url
|
|
271
320
|
placeholderImage: _defaultSource
|
|
272
321
|
options: options
|
|
@@ -276,6 +325,18 @@
|
|
|
276
325
|
NSError* _Nullable error,
|
|
277
326
|
SDImageCacheType cacheType,
|
|
278
327
|
NSURL* _Nullable imageURL) {
|
|
328
|
+
// The download was running when the app went to the
|
|
329
|
+
// background, and failed: iOS suspends it there, and its
|
|
330
|
+
// timeout keeps counting, so it times out as the app comes
|
|
331
|
+
// back (NSURLErrorTimedOut), or loses its connection. Load it
|
|
332
|
+
// again once the app is active, as Android does (#758).
|
|
333
|
+
if (error && [error.domain isEqualToString: NSURLErrorDomain] &&
|
|
334
|
+
FFFEnteredBackgroundAt > startedAt && !weakSelf.retriedAfterBackground &&
|
|
335
|
+
weakSelf.source == source) {
|
|
336
|
+
weakSelf.retriedAfterBackground = YES;
|
|
337
|
+
[weakSelf downloadWhenActive: source options: options context: context];
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
279
340
|
if (error) {
|
|
280
341
|
weakSelf.hasErrored = YES;
|
|
281
342
|
if (weakSelf.onFastImageError) {
|
|
@@ -294,7 +355,37 @@
|
|
|
294
355
|
}];
|
|
295
356
|
}
|
|
296
357
|
|
|
358
|
+
// Downloads the source now if the app is active, or once it is (unless
|
|
359
|
+
// another load started meanwhile).
|
|
360
|
+
- (void) downloadWhenActive: (FFFastImageSource*)source options: (SDWebImageOptions)options context: (SDWebImageContext*)context {
|
|
361
|
+
// nil in app extensions, which don't get these notifications.
|
|
362
|
+
UIApplication* application = RCTSharedApplication();
|
|
363
|
+
if (!application || application.applicationState == UIApplicationStateActive) {
|
|
364
|
+
[self downloadImage: source options: options context: context];
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
[self stopWaitingForActive];
|
|
368
|
+
__weak typeof(self) weakSelf = self;
|
|
369
|
+
self.activeObserver = [[NSNotificationCenter defaultCenter] addObserverForName: UIApplicationDidBecomeActiveNotification
|
|
370
|
+
object: nil
|
|
371
|
+
queue: [NSOperationQueue mainQueue]
|
|
372
|
+
usingBlock: ^(NSNotification* notification) {
|
|
373
|
+
[weakSelf stopWaitingForActive];
|
|
374
|
+
if (weakSelf.source == source) {
|
|
375
|
+
[weakSelf downloadImage: source options: options context: context];
|
|
376
|
+
}
|
|
377
|
+
}];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
- (void) stopWaitingForActive {
|
|
381
|
+
if (self.activeObserver) {
|
|
382
|
+
[[NSNotificationCenter defaultCenter] removeObserver: self.activeObserver];
|
|
383
|
+
self.activeObserver = nil;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
297
387
|
- (void) dealloc {
|
|
388
|
+
[self stopWaitingForActive];
|
|
298
389
|
[self sd_cancelCurrentImageLoad];
|
|
299
390
|
}
|
|
300
391
|
|