I have a component that takes image props and does the proper adjustments (and works within a ScrollView
and with require
d assets. Inside a scroll view it uses the height of the image as the height regardless if it is scaled that causes some excess padding. This component performs size computations and readjusts the image style to work with a 100% width preserving the aspect ratio of the file that was loaded.
import React, { useState } from "react";
import { Image, ImageProps } from "react-native";
export function FullWidthImage(props: ImageProps) {
// Initially set the width to 100%
const [viewDimensions, setViewDimensions] = useState<{
width?: number | string;
height?: number | string;
}>({
width: "100%",
height: undefined,
});
const [imageDimensions, setImageDimensions] = useState<{
width?: number;
height?: number;
}>(() => {
if (typeof props.source === "number") {
// handle case where the source is an asset in which case onLoad won't get triggered
const { width, height } = Image.resolveAssetSource(props.source);
return { width, height };
} else {
return {
width: undefined,
height: undefined,
};
}
});
return (
<Image
onLayout={(e) => {
// this is triggered when the "view" layout is provided
if (imageDimensions.width && imageDimensions.height) {
setViewDimensions({
width: e.nativeEvent.layout.width,
height:
(e.nativeEvent.layout.width * imageDimensions.height) /
imageDimensions.width,
});
}
}}
onLoad={(e) => {
// this is triggered when the image is loaded and we have actual dimensions.
// But only if loading via URI
setImageDimensions({
width: e.nativeEvent.source.width,
height: e.nativeEvent.source.height,
});
}}
{...props}
style={[
props.style,
{
width: viewDimensions.width,
height: viewDimensions.height,
},
]}
/>
);
}
This is to compensate for contain
which adds extra padding around the image (which is basically making the image view height full) even if the image width is 100%
.
Note chances are you are trying to put it in as a background, in which case ImageBackground
does not renders correctly on Android. Using the above code a few tweaks I created the following that renders things correctly with long and short text.
import React, { PropsWithChildren } from "react";
import { ImageProps, View } from "react-native";
import { FullWidthImage } from "./FullWidthImage";
export function FullWidthImageBackground(props: PropsWithChildren<ImageProps>) {
const imageProps = { ...props };
delete imageProps.children;
return (
<View>
<FullWidthImage
{...imageProps}
style={{
position: "absolute",
}}
/>
{props.children}
</View>
);
}
Note if you are using it with a header, you need to add a padding view as the first child
<View
style={{
height: safeAreaInsets.top + (Platform.OS === "ios" ? 96 : 44),
}}
/>