diff --git a/README.md b/README.md index 0138e0f..b87b8e8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ react-native-responsive-screen is a small library that provides 2 simple methods so that React Native developers can code their elements with responsive independent pixel (dp) values. No media queries needed. -It can be easily combined with other CSS libraries for React Native, i.e. [styled components](https://www.styled-components.com/) and [Expo framework](https://expo.io/) as well. +UPDATE: From version 1.1 onwards, it provides an optional third method for screen orienation detection and automatic rerendering according to new dimensions. + +It can be easily combined with other CSS libraries for React Native, i.e. [styled components](https://www.styled-components.com/) and [Expo framework](https://expo.io/) as well. Check out the [Usage](#usage) section below for more details. Give it a try and make your life simpler! @@ -19,17 +21,19 @@ This package provides a way to use percentages - the developer provides percenta `npm install react-native-responsive-screen --save` # Usage -``` + +## 1. How to use with StyleSheet.create() and without orientation change support +```javascript import React, {Component} from 'react'; -import { - StyleSheet, - Text, - View, -} from 'react-native'; +import {StyleSheet, Text, View} from 'react-native'; import {widthPercentageToDP, heightPercentageToDP} from 'react-native-responsive-screen'; class Login extends Component { - this.state = {}; + constructor(props) { + super(props); + + this.state = {}; + } render() { return ( @@ -58,6 +62,107 @@ const styles = StyleSheet.create({ export default Login; ``` +## 2. How to use with StyleSheet.create() and with orientation change support +In odrer to detect orientation change, there are 2 major differences from the previous case: +* we add a listener function in every screen that supports orientation change (and a remove listener function respectively) +* we move the stylesheet creation inside the render function, so that the styles are recalculated whenever we detect an orientation change (and therefore trigger a rerender). + +When using this, make sure to monitor UI performance of your app in a real device on orienation change. Since the styles are calculated every time from scratch inside the render function, make sure it's nor affecting your performance, especially on complicated screens with many UI elements. + +```javascript +import React, {Component} from 'react'; +import {StyleSheet, Text, View} from 'react-native'; +import { + widthPercentageToDP, + heightPercentageToDP, + listenOrientationChange, + removeOrientationListener +} from 'react-native-responsive-screen'; + +class Login extends Component { + constructor(props) { + super(props); + + this.state = {}; + + listenOrientationChange(this); + } + + componentWillUnMount() { + removeOrientationListener(); + } + + render() { + const styles = StyleSheet.create({ + container: { + flex: 1 + }, + textWrapper: { + height: heightPercentageToDP('70%'), // 70% of height device screen + width: widthPercentageToDP('80%') // 80% of width device screen + }, + myText: { + fontSize: heightPercentageToDP('5%') // End result looks like the provided UI mockup + } + }); + + return ( + + + Login + + + ); + } +} + +export default Login; +``` + +## 3. How to use with styled components +Same logic applies as above in case you want to use the package with or without orientation change support. Below se show a sample setup with styled compoments and without orientation change support. + +```javascript +import React, {Component} from 'react'; +import {widthPercentageToDP, heightPercentageToDP} from 'react-native-responsive-screen'; +import styled from 'styled-components/native'; + +class Login extends Component { + constructor(props) { + super(props); + + this.state = {}; + } + + render() { + return ( + + + Login + + + ); + } +} + +const Container = styled.View` + flex: 1; +`; + +const TextWrapper = styled.View` + height: heightPercentageToDP('70%'); // 70% of height device screen + width: widthPercentageToDP('80%'); // 80% of width device screen +`; + +const Login = styled.Text` + font-size: heightPercentageToDP('5%') // End result looks like the provided UI mockup +`; + +export default Login; +``` + + + # How do I know it works for all devices ? As mentioned in ["How to Develop Responsive UIs with React Native"](https://medium.com/building-with-react-native/how-to-develop-responsive-uis-with-react-native-1x03-a448097c9503) article, this solution is already in production apps and is tested with a set of Android, iOS emulators of different screen specs, in order to verify that we always have the same end result. diff --git a/src/index.js b/src/index.js index 6160d0a..0f5bb5a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,14 @@ // packages import { Dimensions, PixelRatio } from 'react-native'; -// Retrieve screen's width once +// Retrieve initial screen's width const screenWidth = Dimensions.get('window').width; -// Retrieve screen's height once +// Retrieve initial screen's height const screenHeight = Dimensions.get('window').height; /** - * Converts provided width percentage to independent pixel (dp). + * Converts provided width percentage to independent pixel (dp). * @param {string} widthPercent The percentage of screen's width that UI element should cover * along with the percentage symbol (%). * @return {number} The calculated dp depending on current device's screen width. @@ -18,7 +18,7 @@ const widthPercentageToDP = widthPercent => { const elemWidth = parseFloat(widthPercent); // Use PixelRatio.roundToNearestPixel method in order to round the layout - // size (dp) to the nearest one that correspons to an integer number of pixels. + // size (dp) to the nearest one that correspons to an integer number of pixels. return PixelRatio.roundToNearestPixel(screenWidth * elemWidth / 100); }; @@ -37,7 +37,41 @@ const heightPercentageToDP = heightPercent => { return PixelRatio.roundToNearestPixel(screenHeight * elemHeight / 100); }; +/** + * Event listener function that detects orientation change (every time it occurs) and triggers + * screen rerendering. It does that, by changing the state of the screen where the function is + * called. State changing occurs for a new state variable with the name 'orientation' that will + * always hold the current value of the orientation after the 1st orientation change. + * Invoke it inside the screen's constructor or in componentDidMount lifecycle method. + * @param {object} that Screen's class component this variable. The function needs it to + * invoke setState method and trigger screen rerender (this.setState()). + */ +const listenOrientationChange = that => { + Dimensions.addEventListener('change', newDimensions => { + // Retrieve and save new dimensions + screenWidth = newDimensions.window.width; + screenHeight = newDimensions.window.height; + + // Trigger screen's rerender with a state update of the orientation variable + that.setState({ + orientation: screenWidth < screenHeight ? 'portrait' : 'landscape' + }); + }); +}; + +/** + * Wrapper function that removes orientation change listener and should be invoked in + * componentWillUnmount lifecycle method of every class component (UI screen) that + * listenOrientationChange function has been invoked. This should be done in order to + * avoid adding new listeners every time the same component is re-mounted. + */ +const removeOrientationListener = () => { + Dimensions.removeEventListener('change', () => {}); +}; + export { widthPercentageToDP, - heightPercentageToDP + heightPercentageToDP, + listenOrientationChange, + removeOrientationListener };