Reload image without dispatch_async if completion handler is already on main thread

Summary:
This works with D3305161 to minimize image flashing. After D3305161, the completion handler passed to `-[RCTImageLoader loadImageWithoutClipping:size:scale:resizeMode:progressBlock:completionBlock:]` may be called back on the main queue in the case of a cached image. In this case, we want to set the image view's image property synchronously rather than on the next runloop iteration via dispatch_async. This minimizes the amount of image flashing the user sees when displaying a cached image.

The exception to this case is for blurred images. A blur can be an expensive (taking multiple ms on the CPU), so we always make sure to perform the blur off the main queue even if the image is cached and the callback came back on the main queue.

Reviewed By: nicklockwood

Differential Revision: D3310176

fbshipit-source-id: 6820782527b65e4956879cf06e8ed2c09c622a58
This commit is contained in:
Ben Nham 2016-05-17 10:41:45 -07:00 committed by Facebook Github Bot 3
parent fe5c0d2d06
commit 5bb5ea7135
1 changed files with 26 additions and 8 deletions

View File

@ -207,13 +207,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
scale:imageScale
resizeMode:(RCTResizeMode)self.contentMode
progressBlock:progressHandler
completionBlock:^(NSError *error, UIImage *image) {
completionBlock:^(NSError *error, UIImage *loadedImage) {
RCTImageView *strongSelf = weakSelf;
if (blurRadius > __FLT_EPSILON__) {
// Do this on the background thread to avoid blocking interaction
image = RCTBlurredImageWithRadius(image, blurRadius);
}
dispatch_async(dispatch_get_main_queue(), ^{
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
if (![source isEqual:strongSelf.source]) {
// Bail out if source has changed since we started loading
return;
@ -234,9 +230,31 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
}
}
if (strongSelf->_onLoadEnd) {
strongSelf->_onLoadEnd(nil);
strongSelf->_onLoadEnd(nil);
}
});
};
if (blurRadius > __FLT_EPSILON__) {
// Blur on a background thread to avoid blocking interaction
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = RCTBlurredImageWithRadius(loadedImage, blurRadius);
RCTExecuteOnMainThread(^{
setImageBlock(image);
}, NO);
});
} else {
// No blur, so try to set the image on the main thread synchronously to minimize image
// flashing. (For instance, if this view gets attached to a window, then -didMoveToWindow
// calls -reloadImage, and we want to set the image synchronously if possible so that the
// image property is set in the same CATransaction that attaches this view to the window.)
if ([NSThread isMainThread]) {
setImageBlock(loadedImage);
} else {
RCTExecuteOnMainThread(^{
setImageBlock(loadedImage);
}, NO);
}
}
}];
} else {
[self clearImage];