From bcd2ef5dfc44818b04b5e79cbcff6658fa2ffa70 Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Wed, 23 Sep 2015 07:30:37 -0700 Subject: [PATCH] Improve RCTImageView resizing logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: @​public It's less important to reload for downscaling than upscaling, so increase the threshold in that direction. Also, this now considers each dimension separately, and a big enough change either way should result in a reload. Finally, this considers both the current image size and the inflight target size, so we don't reload if either is already good. Reviewed By: @tadeuzagallo Differential Revision: D2470911 --- Libraries/Image/RCTImageView.m | 39 +++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index 69850e0b3..52742cb1c 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -18,6 +18,21 @@ #import "UIView+React.h" +/** + * Determines whether an image of `currentSize` should be reloaded for display + * at `idealSize`. + */ +static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSize) { + static const CGFloat upscaleThreshold = 1.2; + static const CGFloat downscaleThreshold = 0.5; + + CGFloat widthMultiplier = idealSize.width / currentSize.width; + CGFloat heightMultiplier = idealSize.height / currentSize.height; + + return widthMultiplier > upscaleThreshold || widthMultiplier < downscaleThreshold || + heightMultiplier > upscaleThreshold || heightMultiplier < downscaleThreshold; +} + @interface RCTImageView () @property (nonatomic, copy) RCTDirectEventBlock onLoadStart; @@ -146,8 +161,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) { [self cancelImageLoad]; - if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) { - + if (_src && self.frame.size.width > 0 && self.frame.size.height > 0) { if (_onLoadStart) { _onLoadStart(nil); } @@ -199,15 +213,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) _targetSize = frame.size; [self reloadImage]; } else if ([RCTImageView srcNeedsReload:_src]) { - CGSize idealSize = RCTTargetSize(self.image.size, self.image.scale, frame.size, - RCTScreenScale(), self.contentMode, YES); - CGFloat widthChangeFraction = ABS(_targetSize.width - idealSize.width) / _targetSize.width; - CGFloat heightChangeFraction = ABS(_targetSize.height - idealSize.height) / _targetSize.height; + CGSize imageSize = self.image.size; + CGSize idealSize = RCTTargetSize(imageSize, self.image.scale, frame.size, RCTScreenScale(), self.contentMode, YES); - // If the combined change is more than 20%, reload the asset in case there is a better size. - if (widthChangeFraction + heightChangeFraction > 0.2) { - _targetSize = idealSize; - [self reloadImage]; + if (RCTShouldReloadImageForSizeChange(imageSize, idealSize)) { + if (RCTShouldReloadImageForSizeChange(_targetSize, 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 { + // Our existing image is good enough. + [self cancelImageLoad]; + _targetSize = imageSize; } } }