0

I have an image I'd like to be full width of the device screen.

Using just a View I can achieve this:

<View style={{flex:1}}>
     <Image resizeMode="contain" style={{flex: 1, height: undefined, width: undefined}} source={this.props.images.infoImage} />
</View>

Changing this to a ScrollView causes the image to stop rendering completely:

<ScrollView style={{flex:1}}>
     <Image resizeMode="contain" style={{flex: 1, height: undefined, width: undefined}} source={this.props.images.infoImage} />
</ScrollView>

I found that if I set a height on the image it would render...but I want the image to dynamically respond to the size of the device.

How can I solve this without declaring an explicit height?

callmetwan
  • 1,205
  • 2
  • 14
  • 31

3 Answers3

6

As per @martinarroyo's comment I added contentContainerStyle={{flex:1}} to the ScrollView, like so:

<ScrollView contentContainerStyle={{flex:1}}>
    <Image resizeMode="contain" style={{flex: 1, width: undefined, height: undefined}} source={this.props.images.infoImage} />
</ScrollView

This completely resolved my issue. The image is the full width of the display while maintaining the correct aspect ratio.

EDIT: Turns out the image frame height stays the same but the image scales down. This means it has large chunks of padding on the top and bottom. As of now I don't know how to remove it.

EDIT 2:

It seems there aren't any tools out of the box with RN and ultimately ended up using a modified version of @Ihor Burlachenko's solution. I created a custom component that takes desired width/height constraints and scales it based on that. I need to use this for local files, which Image.getSize() method does not work on, so I modified Ihor's solution to allow for that by passing in a dimensions object that contain width and height.

import React from 'react';
import { Image } from 'react-native';

export default class ScalableImage extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            width: null,
            height: null,
        }
    }

    componentDidMount() {
        if(typeof this.props.source === 'string') {
            Image.getSize(this.props.source, this._calculateImageDimensions, console.log);
        } else if(this.props.dimensions) {
            this._calculateImageDimensions(this.props.dimensions.width, this.props.dimensions.height)
        }
    }

    render() {
        return (
            <Image
                { ...this.props }
                style={[
                    this.props.style,
                    { width: this.state.width, height: this.state.height }
                ]}
            />
        );
    }

    _calculateImageDimensions(width, height) {
        let ratio;

        if (this.props.width && this.props.height) {
            ratio = Math.min(this.props.width / width, this.props.height / height);
        }
        else if (this.props.width) {
            ratio = this.props.width / width;
        }
        else if (this.props.height) {
            ratio = this.props.height / height;
        }

        this.setState({ width: width * ratio, height: height * ratio });
    }
}

ScalableImage.propTypes = {
    width: React.PropTypes.number,
    height: React.PropTypes.number,
    dimensions: React.PropTypes.object
};

I then use it as so:

<ScalableImage source={require('../path/to/local/image.png')} 
               width={Dimensions.get('window').width()} 
               dimensions={{width: 700, height: 400}} />
James Allardice
  • 164,175
  • 21
  • 332
  • 312
callmetwan
  • 1,205
  • 2
  • 14
  • 31
  • Hey @callmetwan, If I want to do something like this? how can achieve these? it's a horizontal ScrollView and when the item focused it's scaled or something like this. https://imgur.com/gaMxyTP – DevAS Sep 03 '19 at 23:53
0

As a last resource, you can use Dimensions.

Get the windows's width and height on every render, that way you'll account for changes in size (basically, rotations) and will match every device.

You can use it like this:

import {Dimensions} from 'react-native'+
// Then, in your component:
render(){
    const {width, height} = Dimensions.get('window'):
    return (<ScrollView style={{flex:1}}>
     <Image resizeMode="contain" style={{flex: 1, height, width}} source={this.props.images.infoImage} />
</ScrollView>)
}
martinarroyo
  • 9,389
  • 3
  • 38
  • 75
  • This will make the image the full height of the device which I'm not looking to do. Essentially I'm looking for the equivalency of how `max-width: 100%` works in the browser. – callmetwan Mar 01 '17 at 15:56
  • Mmm... you are right. Also I noticed that you are not using `contentContainerStyle` in your ``. Try setting it to `contentContainerStyle={{flex:1}}`. – martinarroyo Mar 01 '17 at 16:54
  • Adding `contentContainerStyle={{flex:1}}` did it!! – callmetwan Mar 01 '17 at 18:46
  • Hey @martinarroyo, If I want to do something like this? how can achieve these? it's a horizontal ScrollView and when the item focused it's scaled or something like this. https://imgur.com/gaMxyTP – DevAS Sep 03 '19 at 23:53
0

I ended up wrapping React Image inside custom Image component which calculates height dynamically to keep the image ratio in onComponentDidMount:

Image.getSize(this.props.source.uri, (width, height) => {
    let ratio;

    if (this.props.width && this.props.height) {
        ratio = Math.min(this.props.width / width, this.props.height / height);
    }
    else if (this.props.width) {
        ratio = this.props.width / width;
    }
    else if (this.props.height) {
        ratio = this.props.height / height;
    }

    this.setState({ width: width * ratio, height: height * ratio });
}, console.log);

And then I use it like that:

<Image width={Dimensions.get('window').width} source={{uri: '<image uri>'}} />
Ihor Burlachenko
  • 4,689
  • 1
  • 26
  • 25
  • Hey @IhorBurlachenko, If I want to do something like this? how can achieve these? it's a horizontal ScrollView and when the item focused it's scaled or something like this. https://imgur.com/gaMxyTP – DevAS Sep 03 '19 at 23:53