mirror of
https://github.com/status-im/react-native.git
synced 2025-01-25 00:39:03 +00:00
fe4b4c2d83
Summary: @public Our background color propagation mechanism is designed to make rendering of translucent content more efficient by pre-blending against an opaque background. Currently this only works for text however, because images are not composited into their background even if the background color is opaque. This diff precomposites network images with their background color when the background is opaque, allowing them to take advantage of this performance optimization. I've also added some logic to correctly crop the downloaded image when the resizeMode is "cover" or "contain" - previously it was only correct for "stretch". Before:{F22437859} After:{F22437862} Test Plan: Run the UIExplorer "<ListView> - Paging" example with "color blended layers" enabled and observe that the images appear in green now, instead of red as they did before.
162 lines
4.8 KiB
Objective-C
162 lines
4.8 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTNetworkImageView.h"
|
|
|
|
#import "RCTConvert.h"
|
|
#import "RCTGIFImage.h"
|
|
#import "RCTImageDownloader.h"
|
|
#import "RCTUtils.h"
|
|
#import "UIView+React.h"
|
|
|
|
@implementation RCTNetworkImageView
|
|
{
|
|
BOOL _deferred;
|
|
NSURL *_imageURL;
|
|
NSURL *_deferredImageURL;
|
|
NSUInteger _deferSentinel;
|
|
RCTImageDownloader *_imageDownloader;
|
|
id _downloadToken;
|
|
}
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame imageDownloader:(RCTImageDownloader *)imageDownloader
|
|
{
|
|
if ((self = [super initWithFrame:frame])) {
|
|
_deferSentinel = 0;
|
|
_imageDownloader = imageDownloader;
|
|
self.userInteractionEnabled = NO;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSURL *)imageURL
|
|
{
|
|
// We clear our imageURL when we are not in a window for a while,
|
|
// to make sure we don't consume network resources while offscreen.
|
|
// However we don't want to expose this hackery externally.
|
|
return _deferred ? _deferredImageURL : _imageURL;
|
|
}
|
|
|
|
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
|
{
|
|
super.backgroundColor = backgroundColor;
|
|
[self _updateImage];
|
|
}
|
|
|
|
- (void)reactSetFrame:(CGRect)frame
|
|
{
|
|
[super reactSetFrame:frame];
|
|
[self _updateImage];
|
|
}
|
|
|
|
- (void)_updateImage
|
|
{
|
|
[self setImageURL:_imageURL resetToDefaultImageWhileLoading:NO];
|
|
}
|
|
|
|
- (void)setImageURL:(NSURL *)imageURL resetToDefaultImageWhileLoading:(BOOL)reset
|
|
{
|
|
if (![_imageURL isEqual:imageURL] && _downloadToken) {
|
|
[_imageDownloader cancelDownload:_downloadToken];
|
|
_downloadToken = nil;
|
|
}
|
|
|
|
_imageURL = imageURL;
|
|
|
|
if (_deferred) {
|
|
_deferredImageURL = imageURL;
|
|
} else {
|
|
if (!imageURL) {
|
|
self.layer.contents = nil;
|
|
return;
|
|
}
|
|
if (reset) {
|
|
self.layer.contentsScale = _defaultImage.scale;
|
|
self.layer.contents = (__bridge id)_defaultImage.CGImage;
|
|
self.layer.minificationFilter = kCAFilterTrilinear;
|
|
self.layer.magnificationFilter = kCAFilterTrilinear;
|
|
}
|
|
if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
|
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) {
|
|
if (data) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (imageURL != self.imageURL) {
|
|
// Image has changed
|
|
return;
|
|
}
|
|
CAKeyframeAnimation *animation = RCTGIFImageWithData(data);
|
|
self.layer.contentsScale = 1.0;
|
|
self.layer.minificationFilter = kCAFilterLinear;
|
|
self.layer.magnificationFilter = kCAFilterLinear;
|
|
[self.layer addAnimation:animation forKey:@"contents"];
|
|
});
|
|
} else if (error) {
|
|
RCTLogWarn(@"Unable to download image data. Error: %@", error);
|
|
}
|
|
}];
|
|
} else {
|
|
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() resizeMode:self.contentMode backgroundColor:self.backgroundColor block:^(UIImage *image, NSError *error) {
|
|
if (image) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (imageURL != self.imageURL) {
|
|
// Image has changed
|
|
return;
|
|
}
|
|
[self.layer removeAnimationForKey:@"contents"];
|
|
self.layer.contentsScale = image.scale;
|
|
self.layer.contents = (__bridge id)image.CGImage;
|
|
});
|
|
} else if (error) {
|
|
RCTLogWarn(@"Unable to download image. Error: %@", error);
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)setImageURL:(NSURL *)imageURL
|
|
{
|
|
[self setImageURL:imageURL resetToDefaultImageWhileLoading:YES];
|
|
}
|
|
|
|
- (void)willMoveToWindow:(UIWindow *)newWindow
|
|
{
|
|
[super willMoveToWindow:newWindow];
|
|
if (newWindow != nil && _deferredImageURL) {
|
|
// Immediately exit deferred mode and restore the imageURL that we saved when we went offscreen.
|
|
[self setImageURL:_deferredImageURL resetToDefaultImageWhileLoading:YES];
|
|
_deferredImageURL = nil;
|
|
}
|
|
}
|
|
|
|
- (void)_enterDeferredModeIfNeededForSentinel:(NSUInteger)sentinel
|
|
{
|
|
if (self.window == nil && _deferSentinel == sentinel) {
|
|
_deferred = YES;
|
|
[_imageDownloader cancelDownload:_downloadToken];
|
|
_downloadToken = nil;
|
|
_deferredImageURL = _imageURL;
|
|
_imageURL = nil;
|
|
}
|
|
}
|
|
|
|
- (void)didMoveToWindow
|
|
{
|
|
[super didMoveToWindow];
|
|
if (self.window == nil) {
|
|
__weak RCTNetworkImageView *weakSelf = self;
|
|
NSUInteger sentinelAtDispatchTime = ++_deferSentinel;
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
|
|
[weakSelf _enterDeferredModeIfNeededForSentinel:sentinelAtDispatchTime];
|
|
});
|
|
}
|
|
}
|
|
|
|
@end
|