Skip to content

Instantly share code, notes, and snippets.

@paramaggarwal
Created January 14, 2016 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paramaggarwal/b9baa00fd7bee9a2948f to your computer and use it in GitHub Desktop.
Save paramaggarwal/b9baa00fd7bee9a2948f to your computer and use it in GitHub Desktop.
React Native Rigid Aspect Ratio Component
#import "RCTShadowView.h"
@interface RNTRigidShadowView : RCTShadowView
/**
* Makes the box rigid, and sizes itself in flex with the
* defined aspect ratio of the CGSize rectangle.
*/
@property (nonatomic, assign) CGSize box;
@end
#import "RNTRigidShadowView.h"
@implementation RNTRigidShadowView
/*
* Sets the measure function on the cssNode, when the `box` 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 RCTBoxMeasure(void *context, float width)
{
RNTRigidShadowView *shadowView = (__bridge RNTRigidShadowView *)context;
if (isnan(width)) {
width = shadowView.box.width;
}
CGFloat computedHeight = 0.0f;
if (shadowView.box.width > 0) {
computedHeight = width * (shadowView.box.height / shadowView.box.width);
}
css_dim_t result;
result.dimensions[CSS_WIDTH] = width;
result.dimensions[CSS_HEIGHT] = computedHeight;
return result;
}
- (void)setBox:(CGSize)box
{
_box = box;
self.cssNode->measure = (box.width && box.height) ? RCTBoxMeasure : nil;
[self dirtyLayout];
}
@end
#import "RCTView.h"
@interface RNTRigidView : RCTView
@end
#import "RNTRigidView.h"
@implementation RNTRigidView
@end
#import "RCTViewManager.h"
@interface RNTRigidViewManager : RCTViewManager
@end
#import "RNTRigidViewManager.h"
#import "RNTRigidView.h"
#import "RNTRigidShadowView.h"
@implementation RNTRigidViewManager
RCT_EXPORT_MODULE();
- (RNTRigidView *)view
{
return [[RNTRigidView alloc] initWithFrame:CGRectZero];
}
- (RNTRigidShadowView *)shadowView
{
return [[RNTRigidShadowView alloc] init];
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_SHADOW_PROPERTY(box, CGSize)
@end
@paramaggarwal
Copy link
Author

Here is the generic component I have been using to make an arbitrary container who's height is always in a particular ratio to its width. This width is equal to the width of the parent. Great for use with images where you want them to stick to a particular aspect ratio.

USAGE

First import all the necessary things.

var React = require('react-native');
var {
  Image,
  requireNativeComponent,
} = React;
var RigidView = requireNativeComponent('RNTRigidView');

Now you can first make a RigidView container that will always keep a given height in ratio with the width of its parent. This ratio is defined by the box property.

<RigidView box={box} style={{flex: 1}}>
  <Image
    style={{flex: 1}}
    source={uri: "foo.com/bar.jpg"}
  />
</RigidView>

where box is of the form:

var box = {
  width: 320,
  height: 240,
};

LICENSE: MIT

Feel free to use the code anywhere and also make it into an OSS component.

@polarathene
Copy link

What is the purpose of supplying the box width/height params? If width can be dynamic with flex: 1 and the height is computed by applying the box width/height ratio to the actual width, why not just supply an aspect ratio value instead? The supplied value could still be calculated by providing it as (320/240) or 1.3333333 or (4/3) all resulting in a 4:3 ratio value.

@polarathene
Copy link

I'm not familiar with the codebase, did you specify to only have access to width or is that a limitation? If you could get height of the element passed in too, then you could do something like the following to support sizing the opposite dimension based on only one being defined.

Here is some JS pseudo-code:

let dimensions = [ {dimension: CSS_WIDTH, value: width}, {dimension: CSS_HEIGHT, value: height} ];
// If no height value default to width, else use height as the base
const base = (isnan(height) || height <= 0) ? dimensions.splice(0,1) : dimensions.splice(1,1); // or shift()/pop()

// If base is width verify a valid value. I don't feel good about this, since it'd check height twice, 
// presumably you'd verify at least one dimension was valid prior to running the function, 
// providing a default should belong to a min-width/height property, should either be 0 or throw an error
if (isnan(base.value) || base.value <= 0) {
    base.value = 0
}

// removed the base object, leaving the other to be the computed object
let computed = dimensions[0];
computed.value = base.value / aspectRatio; // eg 320 / 1.3333333 = 240


// return snippet now refers to base/computed dimension property and value to set CSS_WIDTH/HEIGHT props.
css_dim_t result;
result.dimensions[base.dimension] = base.value;
result.dimensions[computed.dimension] = computed.value;
return result;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment