239

Is it possible to get the size (width and height) of a certain view? For example, I have a view showing the progress:

<View ref='progressBar' style={{backgroundColor:'red',flex:this.state.progress}} /> 

I need to know the actual width of the view to align other views properly. Is this possible?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Matthew
  • 4,935
  • 5
  • 21
  • 17

9 Answers9

479

As of React Native 0.4.2, View components have an onLayout prop. Pass in a function that takes an event object. The event's nativeEvent contains the view's layout.

<View onLayout={(event) => {
  var {x, y, width, height} = event.nativeEvent.layout;
}} />

The onLayout handler will also be invoked whenever the view is resized.

The main caveat is that the onLayout handler is first invoked one frame after your component has mounted, so you may want to hide your UI until you have computed your layout.

Abhishek Thapliyal
  • 3,497
  • 6
  • 30
  • 69
ide
  • 19,942
  • 5
  • 64
  • 106
  • 5
    The issue of this approach is when device rotates/view size changes, I do not get onLayout called again. – Matthew May 19 '15 at 20:08
  • 4
    onLayout is invoked when the view's frame changes. Perhaps your view does not change its size or position on rotation. Look at the onLayout example in the UIExplorer, where onLayout is invoked upon rotation. – ide May 19 '15 at 21:36
  • 4
    Let's say I want to display a `View` differently depending on the dimensions. I don't understand how I can use the View's `onLayout` function to change how I display the View. Doesn't that lead to an infinite loop? – Lane Rettig Feb 03 '16 at 16:52
  • 2
    @LaneRettig Yes it does so if this is really what you want to do, then you should write your code in a way that reaches static equilibrium. But it sounds like you might just want to customize your view based on the screen dimensions in which case `onLayout` is unrelated. – ide Feb 03 '16 at 20:14
  • 12
    @ide how would you hide the UI until layout is computed ? – Irfanlone Aug 29 '17 at 19:52
  • 1
    This is just perfect! – pnizzle Nov 22 '17 at 23:25
  • hello , i am coding a PopMenu , is there any way i can get a Component's `margin(marginTop | marginLeft ...)`'s value when `onPress` or `onLayout` be called? thank you... – jiar wang Dec 09 '17 at 06:11
  • 1
    reiterating @Irfanlone's comment, which never got answered: How do you calculate 'potential' size of a view? Any attempt to hide the view will result in `onLayout` giving 0 values for dimensions, since the view is not visible – Felipe May 02 '19 at 18:01
  • @Irfanlone @Felipe, need to embed desired content in `View` that has onLayout. If `onLayout` method called, `setState` `width` and `height`. Desired content need to check to render only if width and height are not undefined. – Thein Jun 23 '19 at 03:29
  • Since View is placed in render function, is there an issue with calling setState() within the onLayout event? – kojow7 Sep 13 '19 at 16:40
  • I am trying to use this to automatically size text to fit a View. It is working good for most of the text, but some of the text keeps jumping back and forth between two different font sizes. I think it is because onLayout is called from within Render, but then in turn it is calling setState. Any ideas how to overcome this issue? – kojow7 Sep 13 '19 at 23:32
  • For me using onLayout cause onPress not to work :( - React 16.9 – chenop Feb 23 '20 at 09:55
40

This is the only thing that worked for me:

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image
} from 'react-native';

export default class Comp extends Component {

  find_dimesions(layout){
    const {x, y, width, height} = layout;
    console.warn(x);
    console.warn(y);
    console.warn(width);
    console.warn(height);
  }
  render() {
    return (
      <View onLayout={(event) => { this.find_dimesions(event.nativeEvent.layout) }} style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('Comp', () => Comp);
Abhishek Kumar
  • 2,136
  • 3
  • 24
  • 36
30

You can easily get the size of the View by onLayout props.

import React from 'react'
import { View } from 'react-native'

export default function index() {
  const onLayout=(event)=> {
    const {x, y, height, width} = event.nativeEvent.layout;
    
  }
  return (
    <View onLayout={onLayout}>
      <OtherComponent />
    </View>
  )
}

The onLayout handler will also be invoked whenever the view is resized.

The main caveat is that the onLayout handler is first invoked one frame after your component has mounted, so you may want to hide your UI until you have computed your layout.

Muhammad Numan
  • 23,222
  • 6
  • 63
  • 80
17

Basically if you want to set size and make it change then set it to state on layout like this:

import React, { Component } from 'react';
import { AppRegistry, StyleSheet, View } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'yellow',
  },
  View1: {
    flex: 2,
    margin: 10,
    backgroundColor: 'red',
    elevation: 1,
  },
  View2: {
    position: 'absolute',
    backgroundColor: 'orange',
    zIndex: 3,
    elevation: 3,
  },
  View3: {
    flex: 3,
    backgroundColor: 'green',
    elevation: 2,
  },
  Text: {
    fontSize: 25,
    margin: 20,
    color: 'white',
  },
});

class Example extends Component {

  constructor(props) {
    super(props);

    this.state = {
      view2LayoutProps: {
        left: 0,
        top: 0,
        width: 50,
        height: 50,
      }
    };
  }

  onLayout(event) {
    const {x, y, height, width} = event.nativeEvent.layout;
    const newHeight = this.state.view2LayoutProps.height + 1;
    const newLayout = {
        height: newHeight ,
        width: width,
        left: x,
        top: y,
      };

    this.setState({ view2LayoutProps: newLayout });
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.View1}>
          <Text>{this.state.view2LayoutProps.height}</Text>
        </View>
        <View onLayout={(event) => this.onLayout(event)} 
              style={[styles.View2, this.state.view2LayoutProps]} />
        <View style={styles.View3} />
      </View>
    );
  }

}


AppRegistry.registerComponent(Example);

You can create many more variation of how it should be modified, by using this in another component which has Another view as wrapper and create an onResponderRelease callback, which could pass the touch event location into the state, which could be then passed to child component as property, which could override onLayout updated state, by placing {[styles.View2, this.state.view2LayoutProps, this.props.touchEventTopLeft]} and so on.

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
juslintek
  • 441
  • 3
  • 12
13

Maybe you can use measure:

measureProgressBar() {
    this.refs.welcome.measure(this.logProgressBarLayout);
},

logProgressBarLayout(ox, oy, width, height, px, py) {
  console.log("ox: " + ox);
  console.log("oy: " + oy);
  console.log("width: " + width);
  console.log("height: " + height);
  console.log("px: " + px);
  console.log("py: " + py);
}
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Yinfeng
  • 5,131
  • 1
  • 18
  • 15
  • For the life of me I can't figure out how to properly use `NativeMethodsMixin`. No matter what I do, `measure` is always undefined. Any pointers? – chandlervdw Aug 24 '16 at 21:34
  • 2
    You don't need to use `NativeMethodsMixin` the function is only available on some elements. For example `TouchableWithFeedback` does have the measure function, but a regular `Touchable` doesn't. Try to change the type of `View` you are using, get the ref and check if the measure element is available. I've stumbled on this aswell. – Jens Jan 17 '17 at 13:53
3

I create this simple component

import React, {Dispatch, SetStateAction} from 'react';
import {View, ViewProps} from 'react-native';

interface GetDimensionsProps {
  children: React.ReactNode | React.ReactNode[];
  onDimensions: Dispatch<SetStateAction<{height: number; width: number}>>;
  viewProps?: ViewProps;
}

export const GetDimensions: React.FC<GetDimensionsProps> = ({
  children,
  onDimensions,
  ...viewProps
}: GetDimensionsProps) => {
  return (
    <View
      onLayout={event =>
        onDimensions({
          width: Math.round(event.nativeEvent.layout.width),
          height: Math.round(event.nativeEvent.layout.height),
        })
      }
      {...viewProps}>
      {children}
    </View>
  );
};

// ────────────────────────────────────────────────────────────────────────────────
// usage

// const [dimensions, setDimensions] = useState<{
//   height: number;
//   width: number;
// }>({width: 0, height: 0});
//
// <GetDimensions onDimensions={setDimensions}>
//  {children}
// </GetDimensions>
Nicolas Sturm
  • 619
  • 5
  • 7
-9

for me setting the Dimensions to use % is what worked for me width:'100%'

Peter Mugendi
  • 565
  • 6
  • 15
-10

You can directly use the Dimensions module and calc your views sizes. Actually, Dimensions give to you the main window sizes.

import { Dimensions } from 'Dimensions';

Dimensions.get('window').height;
Dimensions.get('window').width;

Hope to help you!

Update: Today using native StyleSheet with Flex arranging on your views help to write clean code with elegant layout solutions in wide cases instead computing your view sizes...

Although building a custom grid components, which responds to main window resize events, could produce a good solution in simple widget components

Bruno Guerra
  • 1,199
  • 13
  • 23
  • 1
    This is what I am looking for. Thank you! – Matthew Aug 28 '15 at 21:06
  • 3
    Gah, why isn't this documented more clearly!?! I was trying 'width' and 'height' instead of ('window').width etc. –  Nov 17 '15 at 17:45
  • 1
    @MarkAmery with window dimensions you can planning how your views will look like – Bruno Guerra Nov 29 '15 at 17:34
  • 2
    @BrunoGuerra can you show the code to get a certain view (not window) size by Dimensions? – Spark.Bao May 24 '16 at 08:31
  • 4
    Dimensions does not get updated on many devices when the device is rotated. This is a known [issue](https://github.com/yamill/react-native-orientation/issues/3) and does not seem to be fixed as of react-native 0.32.0-rc.0 – Glenn Lawrence Sep 08 '16 at 08:20
  • I would argue that if you're using height and width frequently to construct views you probably aren't leveraging flex adequately. This also become an issue because it shows window dimensions and NOT usable space. So if your child view only has 50% of space (as restricted by the parent), this approach will cause the screen to overflow. – Coder Lam Feb 01 '18 at 13:42
  • @MJStudio It's useful in fullscreen scenarios. It's always better using modern solutions, this is old answer, but represents one more freely solution. But today using flex or even grid components it's a better solution – Bruno Guerra Feb 18 '20 at 12:59
  • I would second the @Spark.Bao comment that this is the window, not the view dimensions. As for why to get this information? I would like to have some of my views change their appearance (i.e. leave some things out) when they are in a small space. I was thinking of using a different style sheet based on the width of the component, but have not managed to figure out a way to do this. – Steven W. Klassen Sep 01 '20 at 15:41
-10

Here is the code to get the Dimensions of the complete view of the device.

var windowSize = Dimensions.get("window");

Use it like this:

width=windowSize.width,heigth=windowSize.width/0.565

ErikusMaximus
  • 1,150
  • 3
  • 13
  • 28
ravi
  • 370
  • 4
  • 11