Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Libraries/Components/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,21 @@ var View = React.createClass({
]),
style: stylePropType,

/**
* Makes the view resize in a fixed `aspectRatio` as the prop. It can be
* a number or a rectangle. As the width changes due to flex layout,
* the height will change in proportion with same aspect ratio. Use this
* to wrap images and other components, where you would like the aspect
* ratio to be maintained.
*/
aspectRatio: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
width: PropTypes.number,
height: PropTypes.number,
}),
]),

/**
* This is a special performance property exposed by RCTView and is useful
* for scrolling content when there are many subviews, most of which are
Expand Down
13 changes: 13 additions & 0 deletions Libraries/Image/Image.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ var Image = React.createClass({
* image dimensions.
*/
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
/**
* Makes the image resize in a fixed `aspectRatio` as the prop. It can be
* a number or a rectangle. As the width changes due to flex layout,
* the height will change in proportion with same aspect ratio. Use this
* to wrap images where you would like the aspect ratio to be maintained.
*/
aspectRatio: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
width: PropTypes.number,
height: PropTypes.number,
}),
]),
/**
* A unique identifier for this element to be used in UI Automation
* testing scripts.
Expand Down
6 changes: 6 additions & 0 deletions Libraries/ReactNative/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ ReactNativeViewAttributes.RCTView = merge(
// many subviews that extend outside its bound. The subviews must also have
// overflow: hidden, as should the containing view (or one of its superviews).
removeClippedSubviews: true,

// This property makes the view resize in the same aspect ratio as the
// `aspectRatio` rectangle. As the width changes due to flex layout,
// the height will change in proportion with same aspect ratio.
aspectRatio: true,

});

module.exports = ReactNativeViewAttributes;
6 changes: 6 additions & 0 deletions React/Views/RCTShadowView.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry);
@property (nonatomic, assign) css_wrap_type_t flexWrap;
@property (nonatomic, assign) CGFloat flex;

/**
* Makes the box rigid, and sizes itself in flex with the
* defined aspect ratio of the CGSize rectangle.
*/
@property (nonatomic, assign) CGSize aspectRatio;

/**
* Calculate property changes that need to be propagated to the view.
* The applierBlocks set contains RCTApplierBlock functions that must be applied
Expand Down
34 changes: 34 additions & 0 deletions React/Views/RCTShadowView.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,45 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
: 0;
}

/*
* Sets the measure function on the cssNode, when the aspectRatio rectangle is
* set. This helps calculate the height of the node based on width of the parent
* and maintains the aspect ratio of the node.
*/
static css_dim_t RCTAspectRatioMeasure(void *context, float width)
{
RCTShadowView *shadowView = (__bridge RCTShadowView *)context;
if (isnan(width)) {
width = shadowView.aspectRatio.width;
}

CGFloat computedHeight = 0.0f;
if (shadowView.aspectRatio.width > 0) {
computedHeight = width * (shadowView.aspectRatio.height / shadowView.aspectRatio.width);
}

css_dim_t result;
result.dimensions[CSS_WIDTH] = width;
result.dimensions[CSS_HEIGHT] = computedHeight;
return result;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, the aspect ratio would support views that have a known height (either hardcoded height from the stylesheet, or alignSelf: stretch in a horizontal parent) and unknown width. Any way to support this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch. Unfortunately this needs work on the css-layout side. Currently because this was intended for text layout, it only deals with height based on width scenario...


- (void)fillCSSNode:(css_node_t *)node
{
node->children_count = (int)_reactSubviews.count;
}

// Aspect Ratio
- (void)setAspectRatio:(CGSize)aspectRatio {
_aspectRatio = aspectRatio;
if (aspectRatio.width && aspectRatio.height) {
self.cssNode->measure = RCTAspectRatioMeasure;
} else {
self.cssNode->measure = nil;
}
[self dirtyLayout];
}

// The absolute stuff is so that we can take into account our absolute position when rounding in order to
// snap to the pixel grid. For example, say you have the following structure:
//
Expand Down
12 changes: 12 additions & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,16 @@ - (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json

RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL)

RCT_CUSTOM_SHADOW_PROPERTY(aspectRatio, CGSize, RCTShadowView) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you should handle the case where json is nil with defaultView.aspectRatio.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if ([view respondsToSelector:@selector(setAspectRatio:)]) {
if ([json isKindOfClass:NSDictionary.class]) {
[view setAspectRatio:[RCTConvert CGSize:json]];
} else if (json) {
[view setAspectRatio:CGSizeMake([RCTConvert CGFloat:json], 1.0f)];
} else {
[view setAspectRatio:defaultView.aspectRatio];
}
}
}

@end