Avoid sending out multiple requests for the same image
Reviewed By: majak Differential Revision: D3755074 fbshipit-source-id: fea782fcb99e6b977fb52231d378aaab4685d480
This commit is contained in:
parent
dadfe40f55
commit
86fbf2386b
|
@ -52,7 +52,6 @@ static NSDictionary *onLoadParamsForSource(RCTImageSource *source)
|
|||
|
||||
@interface RCTImageView ()
|
||||
|
||||
@property (nonatomic, strong) RCTImageSource *imageSource;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onLoadStart;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onProgress;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onError;
|
||||
|
@ -64,6 +63,13 @@ static NSDictionary *onLoadParamsForSource(RCTImageSource *source)
|
|||
@implementation RCTImageView
|
||||
{
|
||||
__weak RCTBridge *_bridge;
|
||||
|
||||
// The image source that's currently displayed
|
||||
RCTImageSource *_imageSource;
|
||||
|
||||
// The image source that's being loaded from the network
|
||||
RCTImageSource *_pendingImageSource;
|
||||
|
||||
CGSize _targetSize;
|
||||
|
||||
/**
|
||||
|
@ -98,10 +104,10 @@ static NSDictionary *onLoadParamsForSource(RCTImageSource *source)
|
|||
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
|
||||
- (void)updateImage
|
||||
- (void)updateWithImage:(UIImage *)image
|
||||
{
|
||||
UIImage *image = self.image;
|
||||
if (!image) {
|
||||
self.image = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -116,6 +122,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
// Applying capInsets of 0 will switch the "resizingMode" of the image to "tile" which is undesired
|
||||
image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeStretch];
|
||||
}
|
||||
|
||||
// Apply trilinear filtering to smooth out mis-sized images
|
||||
self.layer.minificationFilter = kCAFilterTrilinear;
|
||||
self.layer.magnificationFilter = kCAFilterTrilinear;
|
||||
|
@ -126,9 +133,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
- (void)setImage:(UIImage *)image
|
||||
{
|
||||
image = image ?: _defaultImage;
|
||||
if (image != super.image) {
|
||||
super.image = image;
|
||||
[self updateImage];
|
||||
if (image != self.image) {
|
||||
[self updateWithImage:image];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +156,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
[self reloadImage];
|
||||
} else {
|
||||
_capInsets = capInsets;
|
||||
[self updateImage];
|
||||
[self updateWithImage:self.image];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +165,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
{
|
||||
if (_renderingMode != renderingMode) {
|
||||
_renderingMode = renderingMode;
|
||||
[self updateImage];
|
||||
[self updateWithImage:self.image];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,12 +177,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
}
|
||||
|
||||
- (BOOL)sourceNeedsReload
|
||||
{
|
||||
// If capInsets are set, image doesn't need reloading when resized
|
||||
return UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero);
|
||||
}
|
||||
|
||||
- (void)setResizeMode:(RCTResizeMode)resizeMode
|
||||
{
|
||||
if (_resizeMode != resizeMode) {
|
||||
|
@ -190,7 +190,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
self.contentMode = (UIViewContentMode)resizeMode;
|
||||
}
|
||||
|
||||
if ([self sourceNeedsReload]) {
|
||||
if ([self shouldReloadImageSourceAfterResize]) {
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
|
@ -203,6 +203,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
previousCancellationBlock();
|
||||
_reloadImageCancellationBlock = nil;
|
||||
}
|
||||
|
||||
_pendingImageSource = nil;
|
||||
}
|
||||
|
||||
- (void)clearImage
|
||||
|
@ -229,6 +231,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
if (![self hasMultipleSources]) {
|
||||
return _imageSources.firstObject;
|
||||
}
|
||||
|
||||
// Need to wait for layout pass before deciding.
|
||||
if (CGSizeEqualToSize(size, CGSizeZero)) {
|
||||
return nil;
|
||||
|
@ -252,9 +255,19 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return bestSource;
|
||||
}
|
||||
|
||||
- (BOOL)desiredImageSourceDidChange
|
||||
- (BOOL)shouldReloadImageSourceAfterResize
|
||||
{
|
||||
return ![[self imageSourceForSize:self.frame.size] isEqual:_imageSource];
|
||||
// If capInsets are set, image doesn't need reloading when resized
|
||||
return UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero);
|
||||
}
|
||||
|
||||
- (BOOL)shouldChangeImageSource
|
||||
{
|
||||
// We need to reload if the desired image source is different from the current image
|
||||
// source AND the image load that's pending
|
||||
RCTImageSource *desiredImageSource = [self imageSourceForSize:self.frame.size];
|
||||
return ![desiredImageSource isEqual:_imageSource] &&
|
||||
![desiredImageSource isEqual:_pendingImageSource];
|
||||
}
|
||||
|
||||
- (void)reloadImage
|
||||
|
@ -262,9 +275,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
[self cancelImageLoad];
|
||||
|
||||
RCTImageSource *source = [self imageSourceForSize:self.frame.size];
|
||||
_imageSource = source;
|
||||
_pendingImageSource = source;
|
||||
|
||||
if (_imageSource && self.frame.size.width > 0 && self.frame.size.height > 0) {
|
||||
if (source && self.frame.size.width > 0 && self.frame.size.height > 0) {
|
||||
if (_onLoadStart) {
|
||||
_onLoadStart(nil);
|
||||
}
|
||||
|
@ -284,7 +297,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero)) {
|
||||
// Don't resize images that use capInsets
|
||||
imageSize = CGSizeZero;
|
||||
imageScale = _imageSource.scale;
|
||||
imageScale = source.scale;
|
||||
}
|
||||
|
||||
__weak RCTImageView *weakSelf = self;
|
||||
|
@ -307,7 +320,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
|
||||
- (void)imageLoaderLoadedImage:(UIImage *)loadedImage error:(NSError *)error forImageSource:(RCTImageSource *)source
|
||||
{
|
||||
if (![source isEqual:_imageSource]) {
|
||||
if (![source isEqual:_pendingImageSource]) {
|
||||
// Bail out if source has changed since we started loading
|
||||
return;
|
||||
}
|
||||
|
@ -323,6 +336,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
|
||||
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
|
||||
self->_imageSource = source;
|
||||
self->_pendingImageSource = nil;
|
||||
|
||||
if (image.reactKeyframeAnimation) {
|
||||
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
|
||||
} else {
|
||||
|
@ -361,27 +377,24 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
{
|
||||
[super reactSetFrame:frame];
|
||||
|
||||
if (!self.image || self.image == _defaultImage) {
|
||||
// If we didn't load an image yet, or the new frame triggers a different image source
|
||||
// to be loaded, reload to swap to the proper image source.
|
||||
if ([self shouldChangeImageSource]) {
|
||||
_targetSize = frame.size;
|
||||
[self reloadImage];
|
||||
} else if ([self sourceNeedsReload]) {
|
||||
} else if ([self shouldReloadImageSourceAfterResize]) {
|
||||
CGSize imageSize = self.image.size;
|
||||
CGSize idealSize = RCTTargetSize(imageSize, self.image.scale, frame.size,
|
||||
RCTScreenScale(), (RCTResizeMode)self.contentMode, YES);
|
||||
|
||||
if ([self desiredImageSourceDidChange]) {
|
||||
// Reload to swap to the proper image source.
|
||||
if (RCTShouldReloadImageForSizeChange(imageSize, idealSize) &&
|
||||
RCTShouldReloadImageForSizeChange(_targetSize, idealSize)) {
|
||||
RCTLogInfo(@"Reloading image %@ as size %@", [_imageSources firstObject].request.URL.absoluteString, NSStringFromCGSize(idealSize));
|
||||
|
||||
// If the existing image or an image being loaded are not the right
|
||||
// size, reload the asset in case there is a better size available.
|
||||
_targetSize = idealSize;
|
||||
[self reloadImage];
|
||||
} else if (RCTShouldReloadImageForSizeChange(imageSize, idealSize)) {
|
||||
if (RCTShouldReloadImageForSizeChange(_targetSize, idealSize)) {
|
||||
RCTLogInfo(@"[PERF IMAGEVIEW] Reloading image %@ as size %@", _imageSource.request.URL.absoluteString, NSStringFromCGSize(idealSize));
|
||||
|
||||
// If the existing image or an image being loaded are not the right
|
||||
// size, reload the asset in case there is a better size available.
|
||||
_targetSize = idealSize;
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +409,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
// requests that have gotten "stuck" from the queue, unblocking other images
|
||||
// from loading.
|
||||
[self cancelImageLoad];
|
||||
} else if (!self.image || self.image == _defaultImage) {
|
||||
} else if ([self shouldChangeImageSource]) {
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue