From f6aa5db0e4bdc0edd07de85f76560a522ea38646 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 22 Jun 2018 07:28:42 -0700 Subject: [PATCH] Fabric: RCTImageComponentView Summary: @public This is iOS-specific implementation of 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 --- .../Image/RCTImageComponentView.h | 21 ++++ .../Image/RCTImageComponentView.mm | 106 ++++++++++++++++++ React/Fabric/Utils/MainQueueExecutor.h | 23 ++++ React/Fabric/Utils/MainQueueExecutor.mm | 29 +++++ 4 files changed, 179 insertions(+) create mode 100644 React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h create mode 100644 React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm create mode 100644 React/Fabric/Utils/MainQueueExecutor.h create mode 100644 React/Fabric/Utils/MainQueueExecutor.mm diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h new file mode 100644 index 000000000..a9bcf7d39 --- /dev/null +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.h @@ -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 + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * UIView class for root component. + */ +@interface RCTImageComponentView : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm new file mode 100644 index 000000000..9e8cb4de4 --- /dev/null +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm @@ -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 +#import +#import +#import +#import +#import + +#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(); + } + _props = props; + + [super updateProps:props oldProps:oldProps]; + + const auto &oldImageProps = *std::dynamic_pointer_cast(oldProps); + const auto &newImageProps = *std::dynamic_pointer_cast(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(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(_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 diff --git a/React/Fabric/Utils/MainQueueExecutor.h b/React/Fabric/Utils/MainQueueExecutor.h new file mode 100644 index 000000000..b43a6d7cd --- /dev/null +++ b/React/Fabric/Utils/MainQueueExecutor.h @@ -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 + +namespace facebook { +namespace react { + +class MainQueueExecutor: + public folly::Executor { + +public: + static MainQueueExecutor &instance(); + + void add(folly::Func function) override; +}; + +} // namespace react +} // namespace facebook diff --git a/React/Fabric/Utils/MainQueueExecutor.mm b/React/Fabric/Utils/MainQueueExecutor.mm new file mode 100644 index 000000000..a80d3be1b --- /dev/null +++ b/React/Fabric/Utils/MainQueueExecutor.mm @@ -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 +#include + +namespace facebook { +namespace react { + +MainQueueExecutor &MainQueueExecutor::instance() { + static auto instance = folly::Indestructible{}; + 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