Add Image resizeMode repeat on iOS

Summary:
This adds a new resize mode for iOS 'repeat' that tiles the image over it's frame. This allow to easily create a view with a repeating background pattern which there is no way to do at the moment without including a bunch of different sized assets.

I'm not 100% sure it should be a resizeMode or a separate prop but I went with resizeMode since it made more sense to me and the are not really any use cases where we'd want to use this with another resizeMode other than 'stretch'.

**Test plan**

Tested mainly by adding a UIExplorer example, also tested that changing the resizeMode prop from and to 'repeat' worked properly.
![screen shot 2016-06-07 at 3 06 17 am](https://cloud.githubusercontent.com/assets/2677334/15848755/d95d8046-2c5c-11e6-9f3d-1ce8a1c9c846.png)

I'd like to implement this on Android too but it is a bit trickier since Fresco's ImageView doesn't support image tiling and would require submitting a PR there too :(
Closes https://github.com/facebook/react-native/pull/7968

Differential Revision: D3469119

Pulled By: javache

fbshipit-source-id: ab9dbfe448a5b0771dbf0c41fcceeb366210f583
This commit is contained in:
Janic Duplessis 2016-06-22 04:13:22 -07:00 committed by Facebook Github Bot 6
parent 0669a38b01
commit 4c83237511
9 changed files with 48 additions and 9 deletions

View File

@ -505,6 +505,18 @@ exports.examples = [
source={image}
/>
</View>
{ Platform.OS === 'ios' ?
<View style={styles.leftMargin}>
<Text style={[styles.resizeModeText]}>
Repeat
</Text>
<Image
style={styles.resizeMode}
resizeMode={Image.resizeMode.repeat}
source={image}
/>
</View>
: null }
{ Platform.OS === 'android' ?
<View style={styles.leftMargin}>
<Text style={[styles.resizeModeText]}>

View File

@ -126,8 +126,11 @@ const Image = React.createClass({
*
* 'stretch': Scale width and height independently, This may change the
* aspect ratio of the src.
*
* 'repeat': Repeat the image to cover the frame of the view. The
* image will keep it's size and aspect ratio. (iOS only)
*/
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat']),
/**
* A unique identifier for this element to be used in UI Automation
* testing scripts.

View File

@ -40,6 +40,12 @@ var ImageResizeMode = keyMirror({
* The image will not be scaled up.
*/
center: null,
/**
* repeat - The image will be repeated to cover the frame of the View. The
* image will keep it's size and aspect ratio.
*/
repeat: null,
});
module.exports = ImageResizeMode;

View File

@ -64,6 +64,7 @@ CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize,
switch (resizeMode) {
case RCTResizeModeStretch:
case RCTResizeModeRepeat:
return (CGRect){CGPointZero, RCTCeilSize(destSize, destScale)};
@ -204,6 +205,10 @@ BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
return destSize.width > sourceSize.width;
}
case RCTResizeModeRepeat:
return NO;
}
}

View File

@ -22,5 +22,6 @@
@property (nonatomic, assign) UIImageRenderingMode renderingMode;
@property (nonatomic, strong) RCTImageSource *source;
@property (nonatomic, assign) CGFloat blurRadius;
@property (nonatomic, assign) RCTResizeMode resizeMode;
@end

View File

@ -95,11 +95,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
image = [image imageWithRenderingMode:_renderingMode];
}
// Applying capInsets of 0 will switch the "resizingMode" of the image to "tile" which is undesired
if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, _capInsets)) {
if (_resizeMode == RCTResizeModeRepeat) {
image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeTile];
} else if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, _capInsets)) {
// Applying capInsets of 0 will switch the "resizingMode" of the image to "tile" which is undesired
image = [image resizableImageWithCapInsets:_capInsets resizingMode:UIImageResizingModeStretch];
}
// Apply trilinear filtering to smooth out mis-sized images
self.layer.minificationFilter = kCAFilterTrilinear;
self.layer.magnificationFilter = kCAFilterTrilinear;
@ -161,10 +162,19 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
return UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero);
}
- (void)setContentMode:(UIViewContentMode)contentMode
- (void)setResizeMode:(RCTResizeMode)resizeMode
{
if (self.contentMode != contentMode) {
super.contentMode = contentMode;
if (_resizeMode != resizeMode) {
_resizeMode = resizeMode;
if (_resizeMode == RCTResizeModeRepeat) {
// Repeat resize mode is handled by the UIImage. Use scale to fill
// so the repeated image fills the UIImageView.
self.contentMode = UIViewContentModeScaleToFill;
} else {
self.contentMode = (UIViewContentMode)resizeMode;
}
if ([self sourceNeedsReload]) {
[self reloadImage];
}
@ -229,7 +239,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
size:imageSize
scale:imageScale
clipped:NO
resizeMode:(RCTResizeMode)self.contentMode
resizeMode:_resizeMode
progressBlock:progressHandler
completionBlock:^(NSError *error, UIImage *loadedImage) {

View File

@ -33,7 +33,7 @@ RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, RCTResizeMode)
RCT_EXPORT_VIEW_PROPERTY(resizeMode, RCTResizeMode)
RCT_EXPORT_VIEW_PROPERTY(source, RCTImageSource)
RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView)
{

View File

@ -13,6 +13,7 @@ typedef NS_ENUM(NSInteger, RCTResizeMode) {
RCTResizeModeCover = UIViewContentModeScaleAspectFill,
RCTResizeModeContain = UIViewContentModeScaleAspectFit,
RCTResizeModeStretch = UIViewContentModeScaleToFill,
RCTResizeModeRepeat = -1, // Use negative values to avoid conflicts with iOS enum values.
};
@interface RCTConvert(RCTResizeMode)

View File

@ -15,6 +15,7 @@ RCT_ENUM_CONVERTER(RCTResizeMode, (@{
@"cover": @(RCTResizeModeCover),
@"contain": @(RCTResizeModeContain),
@"stretch": @(RCTResizeModeStretch),
@"repeat": @(RCTResizeModeRepeat),
}), RCTResizeModeStretch, integerValue)
@end