Fabric: RCTImageComponentView

Summary:
@public
This is iOS-specific implementation of <Image> view.
Not all props and features are supported yet.
Known issues:
 - Animated GIFs;
 - CA transitions during image appearance.

Reviewed By: mdvacca

Differential Revision: D8526570

fbshipit-source-id: a4b1dca583b139b8a09431565a79f051fae67a36
This commit is contained in:
Valentin Shergin 2018-06-22 07:28:42 -07:00 committed by Facebook Github Bot
parent b09457b4d2
commit f6aa5db0e4
4 changed files with 179 additions and 0 deletions

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTViewComponentView.h>
NS_ASSUME_NONNULL_BEGIN
/**
* UIView class for root <Image> component.
*/
@interface RCTImageComponentView : RCTViewComponentView
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,106 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTImageComponentView.h"
#import <fabric/components/image/ImageEventEmitter.h>
#import <fabric/components/image/ImageLocalData.h>
#import <fabric/components/image/ImageProps.h>
#import <fabric/imagemanager/ImageRequest.h>
#import <fabric/imagemanager/ImageResponse.h>
#import <fabric/imagemanager/RCTImagePrimitivesConversions.h>
#import "RCTConversions.h"
#import "MainQueueExecutor.h"
using namespace facebook::react;
@implementation RCTImageComponentView {
UIImageView *_imageView;
SharedImageLocalData _imageLocalData;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.clipsToBounds = YES;
auto defaultProps = ImageProps();
_imageView.contentMode = (UIViewContentMode)RCTResizeModeFromImageResizeMode(defaultProps.resizeMode);
self.contentView = _imageView;
}
return self;
}
- (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
{
if (!oldProps) {
oldProps = _props ?: std::make_shared<const ImageProps>();
}
_props = props;
[super updateProps:props oldProps:oldProps];
const auto &oldImageProps = *std::dynamic_pointer_cast<const ImageProps>(oldProps);
const auto &newImageProps = *std::dynamic_pointer_cast<const ImageProps>(props);
// `resizeMode`
if (oldImageProps.resizeMode != newImageProps.resizeMode) {
if (newImageProps.resizeMode == ImageResizeMode::Repeat) {
// Repeat resize mode is handled by the UIImage. Use scale to fill
// so the repeated image fills the UIImageView.
_imageView.contentMode = UIViewContentModeScaleToFill;
} else {
_imageView.contentMode = (UIViewContentMode)RCTResizeModeFromImageResizeMode(newImageProps.resizeMode);
}
}
// `tintColor`
if (oldImageProps.tintColor != newImageProps.tintColor) {
_imageView.tintColor = [UIColor colorWithCGColor:newImageProps.tintColor.get()];
}
}
- (void)updateLocalData:(SharedLocalData)localData
oldLocalData:(SharedLocalData)oldLocalData
{
_imageLocalData = std::static_pointer_cast<const ImageLocalData>(localData);
assert(_imageLocalData);
auto future = _imageLocalData->getImageRequest().getResponseFuture();
future.via(&MainQueueExecutor::instance()).then([self](ImageResponse &&imageResponse) {
self.image = (__bridge_transfer UIImage *)imageResponse.getImage().get();
});
}
- (void)setImage:(UIImage *)image
{
const auto &imageProps = *std::static_pointer_cast<const ImageProps>(_props);
if (imageProps.tintColor) {
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
if (imageProps.resizeMode == ImageResizeMode::Repeat) {
image = [image resizableImageWithCapInsets:RCTUIEdgeInsetsFromEdgeInsets(imageProps.capInsets)
resizingMode:UIImageResizingModeTile];
} else if (imageProps.capInsets != EdgeInsets()) {
// Applying capInsets of 0 will switch the "resizingMode" of the image to "tile" which is undesired.
image = [image resizableImageWithCapInsets:RCTUIEdgeInsetsFromEdgeInsets(imageProps.capInsets)
resizingMode:UIImageResizingModeStretch];
}
_imageView.image = image;
// Apply trilinear filtering to smooth out mis-sized images.
_imageView.layer.minificationFilter = kCAFilterTrilinear;
_imageView.layer.magnificationFilter = kCAFilterTrilinear;
}
@end

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <folly/Executor.h>
namespace facebook {
namespace react {
class MainQueueExecutor:
public folly::Executor {
public:
static MainQueueExecutor &instance();
void add(folly::Func function) override;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MainQueueExecutor.h"
#include <dispatch/dispatch.h>
#include <folly/Indestructible.h>
namespace facebook {
namespace react {
MainQueueExecutor &MainQueueExecutor::instance() {
static auto instance = folly::Indestructible<MainQueueExecutor>{};
return *instance;
}
void MainQueueExecutor::add(folly::Func function) {
__block folly::Func blockFunction = std::move(function);
dispatch_async(dispatch_get_main_queue(), ^{
blockFunction();
});
}
} // namespace react
} // namespace facebook