Slower performance on iOS release than development - react-native

I've built a React Native iOS app which is fairly basic; it's a few screens which the user can click through to from a 'Home' component, and each one consists of basic components comprising solely Text/View/Image components.
In simulator the app is responsive and there aren't any JS console warnings, however when I do a release to my iPad (Air 2), there's a noticable lag between the home screen and certain other screens. These are notably the screens which have more images on.
I'm wondering if it's because I'm using larger images (the app was designed for the iPad Pro 2) and scaling the images down for use where I want them. The worst offender is a page which has a masonry-style grid of images. There's still only about 30 in a ScrollView there though. Once the component has been shown once the app is much more responsive.
I've already taken steps to optimise my components in terms of using extending PureComponent or using stateless functions wherever possible, and console logging shows me that the touchables are responding immediately, so the delay is definitely at the render time of the component.
N.B. All images are local (loaded via require('./path/to/file')), nothing is being loaded over the network.
Here's an example of the code that populates an array of items for display inside the ScrollView:
...
const items = mediaItems.map((item, index) => {
// scale desired width (1044 for video, 520 for images) based on designed width and device aspect ratio.
const imageWidth = item.video ? (1044/2732) * deviceWidth : (520/2732) * deviceWidth
return (
<TouchableHighlight key={index} onPress={() => onOpen(item)}>
<View style={[styles.gridImageView, { width: imageWidth }]}>
<Image style={styles.gridImage} source={item.image} />
</View>
</TouchableHighlight>
)
});
...
and the styles for the gridImageView and gridImage are as follows:
...
gridImageView: {
height: (460/2732) * width,
margin: (2/2732) * width,
position: 'relative'
},
gridImage: {
resizeMode: 'cover',
width: null,
height: null,
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
...
So my question is kind of multi-layered:
What is the best practice with regards to ensuring the component appears quickly?
Should I not be setting a width/height on the image itself at all?
Should I be doing some kind of pre-loading of the images in the sizes I want before I let the user begin to navigate around the app?
Should I be using JPG instead of PNG images?
Is there another trick I'm missing?

Should I not be setting a width/height on the image itself at all
You have to set a width and height. If you don‘t do so, your image won‘t display
Should I be doing some kind of pre-loading of the images in the sizes I want before I let the user begin to navigate around the app?
It is a good idea to downlod images beforehand. Huge images need a lot of performance. Probably your issues are gone, if you resize your images before displaying them. Therefore you could use react native image resizer
Should I be using JPG instead of PNG images?
You should use JPGs, because they provide a higher compression rate.

Related

React native zoomable scale not work in ios?

I am using react-native-photo-view-ex library to zoom in and out the images. After zooming the image I am getting scale size from onScale callback function.After than I saved this picture and when I want to open saved image again for re-edit with same scale size as I saved the image displayed very small.I adjust minZoom = 0.5 and maxZoom = 10.
This code works perfectly in android and when open images for re-edit in android image displayed on accurate position with given scale. Also I have implemented this feature with gesture handler and ScrollView Zoomable but get same results. Can anyone help me?
<PhotoView
source={{uri: selectedData.url}}
scale={imageScale}
minimumZoomScale={0.5}
maximumZoomScale={10}
onScale={e => {
setImage(e.nativeEvent.scale);
}}
onLoad={() => setImageScale(selectedData.scale, false)}
style={
styles.resizeImageWrapper
}
/>

React native responsive units

I have been watching Rect Native tutorials on youtube and everyone uses units like padding:20 or fontSize:18 etc
Is that how you do in a professional app too? What is the ideal practice? Is this automatically responsive?
No, this is not automatically responsive. If we set padding:20, then the padding of that component stays on 20 no matter what phone we use. The same holds for font sizes, even though they are scaled depending on what pixel density we are dealing with.
A responsive design must be implemented by hand which could be done by using Dimensions as follows.
import { Dimensions } from 'react-native'
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
We can now use a different padding depending on the width of our device.
padding: width > 320 ? 20 : 15
When it comes to general layouting, then flexbox takes care of responsiveness, e.g. imagine that we have three elements on the screen with flexDirection: 'row' and we set flex:1 for all of them, then the available space will be divided equally between them no matter what our screen width is. The same applies for height.
It might be advised to create a separate Fonts file which holds font sizes and similar constants in a common place, so we only need to change them once.
import { Dimensions } from 'react-native'
const width = Dimensions.get('window').width
const height = Dimensions.get('window').height
export Fonts = {
h1: width > 320 ? 18 : 15,
h2: width > 320 ? 16 : 14,
...
}
The above takes care of responsive font sizes and we can use it in all other screens, e.g.
<Text style={{fontSize: Fonts.h1}}>I am a header</Text>

Plotting points on an image and rendering on a different screen size in React Native

So I have an image of a field in which the user is able to plot points by touching their screen. These points are then stored within a database by obtaining the e.nativeEvent.locationX, e.nativeEvent.locationY on the user's press.
So when I plot the point on my Tablet and review where I placed the plot on the field image using my PC, It is displayed incorrectly. I assume this is due to different screen sizes and the image is a different size depending on the device screen size.
How do I resolve this issue so that the plots are consistent no matter what device you are using?
Any other possible solutions are much appreciated.
Below is how I display the image / obtain the user's x-axis and y-axis values and on press these values are retrieved by the function ObtainPosition
<TouchableOpacity onPress={this.ObtainPosition}>
<Image style = {{
width:wp('60%'),
height:hp('100%'),
resizeMode: 'contain'}} source={require('./pitch.png')}/>
</TouchableOpacity>
note - you may see wp and hp this is an import
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
Below is how I display user's plots (Note this is usually on a different screen/component). These plots are retrieved from the database and displayed in an array of objects. As you can see below the .map function loops through the array called PlotsArray and retrieves the x and y values and returns a View which is our plots.
<View>
<Image style = {{
width:wp('60%'),
height:hp('100%'),
resizeMode: 'contain'}} source={require('./pitch.png')}/>
{this.state.PlotsArray.map((data) => {
return (
<View
style={{
position: 'absolute',
left: data.x,
top: data.y,
backgroundColor:'#242424',
width: 10,
height: 10,
borderRadius: 50
}}>
</View>
)
})}
</View>
I think your obtainPosition function will need to take into account the Image element's left and right values (from the window / page) at the same time as you getting the actual x y coordinates of the click (if its padded, or the user is scrolled etc.)
You will also need to get the size of the image when you log the coordinates. So for example have a function similar to:
(event) => {
var {height, url, width} = event.nativeEvent.source;
}
called within the onLoad prop of the image. That way when you store the x, y co-ordinates you can also record the size of the image at the time (say 200px x 200px). Later on, when you render the clicks overlaid on the image, you can asses the size of the image on the 'viewing' device and adjust x y coordinates accordingly or store them as percentages rather than fixed pixels.
Rough example here of how using % on top and left values can adjust to the image varying in size https://jsfiddle.net/skfroxyt/ (have a play with editing the image height and width)

How do I handle scaling issues in a React Native app between tvOS and Android TV?

Native resolution for Apple TV seems to be 1920x1080 (as expected) but for Android TV / Fire TV it seems to be 961.5022957581195x540.8450413639423 (according to Dimensions.get('window')).
So, when I run my app on Apple TV everything looks fine. But when I run it on an Android TV nothing fits on the screen.
Is there a way to force the Android TV to shrink everything? Or do I have to create two different style sheets for the different devices to change font sizes and dimensions of all my components?
We use a different approach, on the base Application class for tv we add this
class TvApplication extends Application {
#Override
protected void attachBaseContext(Context base) {
Configuration configuration = new Configuration(base.getResources().getConfiguration());
configuration.densityDpi = configuration.densityDpi / 2;
Context newContext = base.createConfigurationContext(configuration);
super.attachBaseContext(newContext);
}
}
with this, we have consistent width & height when using dimensions, and we can use the same styling values on all platforms without doing any manipulation on the JS side.
it's not perfect, but it's more convenient when building for multiple platforms
Use Platform.OS to check for platform and use margin property in styles to get the content right on screen in android. This is normal behavior in android tv.
You have PixelRatio and Dimensions for this purpose in React Native. Along with this you need to use a RN module react-native-pixel-perfect, this keeps your app pixel perfect across all devices, quickly and easily
import {PixelRatio, Dimensions} from 'react-native';
import {create} from 'react-native-pixel-perfect';
let displayProps = {
width: PixelRatio.roundToNearestPixel(
Dimensions.get('window').width * PixelRatio.get(),
),
height: PixelRatio.roundToNearestPixel(
Dimensions.get('window').height * PixelRatio.get(),
),
};
let perfectSize = create(displayProps);
Now always pass your size in pixels to this method to get original device pixels based on the devices.
const styles = StyleSheet.create({
container: {
width: perfectSize(500),
height: perfectSize(300)
}
});
Your container will adapt to the devices correctly. Based on their screen resolution.
In case if you have a minimum height x width to support but some devices are less than the minimum screen resolution and you want to still achieve the same results in those devices. Then you can set the minimum screen resolution on this function like below.
let displayProps = {
width: PixelRatio.roundToNearestPixel(
Math.max(1920, Dimensions.get('window').width * PixelRatio.get()),
),
height: PixelRatio.roundToNearestPixel(
Math.max(1080, Dimensions.get('window').height * PixelRatio.get()),
),
};
So in my case if the screen resolution is less than 1920x1080 lets say 720p devices then this will help rendering the UI in 1920x1080.

How to dynamically set height of view based on its width by keeping aspect ratio?

I want to have a view whose width is responsive to its container width while keeping its aspect ratio.
I don't want to hard code width or height, but keep it dynamic and responsive so that it can be reused in multiple situations as a reusable component.
I hope it works similar to the following code:
<View style={{
alignSelf: 'stretch',
aspectRatio: 1.5
}} />
It should fill its container's width and set its own height dynamically based on its width.
Is this possible in react-native with flexbox?
As Hendy Irawan commented, React Native added aspectRatio property to StyleSheet.
https://facebook.github.io/react-native/docs/layout-props.html#aspectratio
From the document:
aspectRatio?: number
Aspect ratio control the size of the undefined dimension of a node.
Aspect ratio is a non-standard property only available in react native
and not CSS.
On a node with a set width/height aspect ratio control the size of the
unset dimension On a node with a set flex basis aspect ratio controls
the size of the node in the cross axis if unset On a node with a
measure function aspect ratio works as though the measure function
measures the flex basis On a node with flex grow/shrink aspect ratio
controls the size of the node in the cross axis if unset Aspect ratio
takes min/max dimensions into account
Thus the following should work:
const styles = StyleSheet.create({
view: {
aspectRatio: 1.5,
}
});
// later in render()
<View style={styles.view} />
Yes, you can use the Dimensions API to get the window's width, and then set height programatically. I typically call the dimensions as soon as the app loads, before you need to know the width, and then just pass the object around as needed.
This could be a custom component to accomplish your use case.
import {Dimensions} from 'react-native'
...
const AspectView = ({style, aspectRatio, children, ...props}) => {
let {height, width} = Dimensions.get('window');
return (
<View style={[style, {height: width * aspectRatio}] {...props}>{children}</View>
)
}