facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.28k stars 24.35k forks source link

Full width image using flexbox? #950

Closed joshbedo closed 9 years ago

joshbedo commented 9 years ago

I'm attempting to make an image full-width and came across a few stackoverflow questions related where the top upvoted answer was to use flexbox similarly to how i'm using it below. Although it doesn't seem to display the image unless i set static widths on my styles property. Any ideas?

var styles = StyleSheet.create({
  canvasContainer: {
    flex:1,
    alignItems: 'stretch'
  },
  canvas: {
    flex:1
   // width: 200,
   // height: 200
  }
});
render: function() {
    return (
      <View style={styles.container}>
        <View style={styles.canvasContainer}>
          <Image
            source={{uri: 'http://imgs.steps.dragoart.com/how-to-draw-a-pony-step-7_1_000000053055_5.jpg'}}
            style={styles.canvas} />
        </View>
        <ScrollView
          style={styles.scrollView}
          scrollEventThrottle={200}
          contentInset={{top: 0}}
          >
          {this.state.messages.map(m => {
            return <Text style={styles.message}>Josh: {m}</Text>
          })}
        </ScrollView>
      </View>
    )
  }
brentvatne commented 9 years ago
canvas: {
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0,
},
<Image
  resizeMode="contain"
  source={{uri: 'http://imgs.steps.dragoart.com/how-to-draw-a-pony-step-7_1_000000053055_5.jpg'}}
  style={styles.canvas} />

Try out stretch and cover for alternative resizeModes too

brentvatne commented 9 years ago

Here's a full example that integrates it using position: relative in the parent to constrain it:

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
} = React;

var SampleApp = React.createClass({
  render: function() {
    return (
      <View style={{flex: 1, flexDirection: 'row'}}>
        <View style={styles.container}>
          <Image
            resizeMode="contain"
            source={{uri: 'http://imgs.steps.dragoart.com/how-to-draw-a-pony-step-7_1_000000053055_5.jpg'}}
            style={styles.canvas} />
        </View>
        <View style={styles.container}>
          <Text>Hello!</Text>
        </View>
      </View>

    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    position: 'relative'
  },
  canvas: {
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  },
});

AppRegistry.registerComponent('SampleApp', () => SampleApp);

screen shot 2015-04-21 at 8 56 29 am

brentvatne commented 9 years ago

If this doesn't resolve your problem feel free to re-open.

joshbedo commented 9 years ago

@brentvatne awesome, i actually solved it by changing the container styles around. alignItems:'center' was causing the child image element to not show for some reason.

miracle2k commented 9 years ago

I can confirm the alignItems fix as well. In general, I find it very fiddly to figure out how combination of properties works, and the results are different depending on whether the source is local or not.

I believe that https://github.com/facebook/react-native/issues/2804#issuecomment-148723052 might be related to this also.

onchainguy-btc commented 8 years ago

Seems like you guys were struggeling with the same thing as me. I have an image not showing up though my code pretty much seems to be equal to @joshbedo's first post (see code below). As soon as I add a fixed height like for instance 300 to my imageWrapper style the image shows up. Does anybody have an idea?

import React, { Component, Image, PropTypes, View } from 'react-native'
import Dimensions from 'Dimensions'

let window = Dimensions.get('window')

const prefix = 'http://my-image-server.com/images/'

class MyImage extends Component {
  render() {
    let imageUrl = prefix + this.props.type + '/' + (this.props.size || 1080) + '/' + this.props.filename + '.jpg'

    return (
      <View style={styles.imageWrapper}>
        <Image style={styles.image} source={{uri: imageUrl}}/>
      </View>
    )
  }
}

MyImage.propTypes = {
  type: PropTypes.string.isRequired,
  filename: PropTypes.string.isRequired,
  size: PropTypes.string
}

let styles = {
  imageWrapper: {
    flex: 1,
    alignItems: 'stretch'
  },
  image: {
    flex: 1
  }
}

export default MyImage
yashvekaria commented 8 years ago

I have done according to this, Hope this code snippet might help you.

<Image style={styles.bgContainer} resizeMode='cover' source={require('./img/bg.png')} />

const styles = StyleSheet.create({ bgContainer: { flex:1, width: null, height: null } });

tisalph commented 8 years ago

Above @yashvekaria solution worked for me

clinyong commented 8 years ago

I have done without flex.

local image

<View>
  <Image
    style={{height: 100, width: null}}
    source={require('./ad.png')}
  />
</View>

network image

<View>
  <Image
    style={{height: 100}}
    source={{uri: 'http://xxx.com/ad.png'}}
  />
</View>
mtolly commented 8 years ago

@yashvekaria's and @clinyong's solution (adding a width: null) worked for me, and I am very curious as to why. Why is setting it to null different from not setting it at all?

vinpac commented 8 years ago

@clinyong How would you do width: 100%, height: auto in react-native?

PierBover commented 8 years ago

@vini175pa you can use react-native-extended-stylesheet for that, but the problem is that when the devices rotates the values are not updated...

superandrew213 commented 8 years ago

@vinpac try this:

Get image width and height with:

Image.getSize(path, ({ width, height }) => {
  this.setState({ width, height });
});

Then do this:

<Image
  source={{...}}
  style={{
    width: Dimensions.get('window').width,
    height: Dimensions.get('window').width * this.state.height / this.state.width
  }}
  resizeMode={'cover'}
/>

Not a pretty way, but it works.

ragamufin commented 8 years ago

Hi,

I ran into the same issue as well. Some of the solutions didn't quite work for me. For instance the previous solution by @superandrew213 would have the image filling the width of the window but often you want the image to fill/respect the width of its parent container. Here's what I tried that works:

<View style={{flexDirection:'row'}}>
    <Image 
        source={...} 
        resizeMode="contain" 
        style={{flexShrink:1}}
     />
</View>

The key point is to get the image to be in a row layout using flexDirection:'row' in its container then the image has resizeMode='contain' in order for it to maintain its aspect ratio and style with flexShrink:1 to make sure it doesn't horizontally overflow its container.

monzerhijazi commented 7 years ago

Hmm, I noticed that these examples only worked when a flex has been specified for all parent views/components. The image itself needs flex: 1, width: null, height: null at a minimum. Anyways this worked for me to display the image on the top half of the screen:

<View style={{flex: 1}}>
        <View style={{flexDirection: "row", flex: 1}}>
          <Image source={{uri: "https://vestigo.co/images/yoga-outside.jpg"}} style={{flex: 1, resizeMode: "stretch", width: null, height: null}}  />
        </View>
        <View style={{flex: 2}}>
        </View>
</View>
dc-me commented 7 years ago

@joshbedo the parent alignItems sets to center , cause the Image not show , does any one knows why? change the parent alignItems and flex:1 for the Image fix the problem for me .

ragamufin commented 7 years ago

@danywheeler If you set alignItems to "center" on the parent then it changes it from the default "stretch". This causes the image then to not have any space if you don't specify a width and height

magician11 commented 7 years ago

@clinyong your method works great in my iOS simulator.

When I view it via Expo on my Android phone I get

Error while updating property 'width' in shadow node of type: RCTTextInlineImage

Looks like we can't use null. Stackoverflow answer here.

Your solution was so slick.. I'd like to keep using it. Ideas? Is this an Android thing?

maludwig commented 7 years ago

I tried wiping out as many style options as possible and finding the minimal set of styles required to reach the goal. You don't need containers or anything, just an Image. Apparently the "width:null" bit is important:

    <Image 
      style={{ width: null }} 
      resizeMode="contain"
      source={require('./img/catfish.jpg')} 
    />

If you need it to fit with other elements:

    <Image 
      style={{ flex:1, width: null }} 
      resizeMode="contain"
      source={require('./img/catfish.jpg')} 
    />
magician11 commented 7 years ago

Following on from @maludwig 's solution, to get a 100% width image that has a specific height, this works:

<Image
    source={require('../images/space.jpeg')}
    style={{ height: 290, width: null }}
    resizeMode="cover"
/>
jwtea commented 7 years ago

Posting this here in case anyone gets in the same trouble as i have. I wanted to be able to put an image in a container and have it fill to that container. The following seems to work so far in any situation. The view wrapping the image with flexDirection of row is what it all hinges on which may be me just being bad still getting used to flex positioning.

    <View 
    style={{flex:1,flexDirection: 'row'}}>
      <Image
        resizeMode='contain'
        style={{
          flex:1,
          width:null,
          height:null}}
        source={Images[status]}
      />
    </View>
hailie-rei commented 7 years ago

@jwtea Thank you! Your variant works for me, but I changed resize mode to 'cover' and now it works perfect!

Noitidart commented 7 years ago

Getting <Image> width:100% and height auto is so finicky. Trying on Android right now. Always such a battle.

ragamufin commented 7 years ago

What I do now is have an image component that based on the width/height of the image it sets the flex properties and such so that I can achieve width 100% without changing the aspect ratio and without the image overflowing its container. The source for my images are set with require('...') so Image.getSize() doesn't work on them but the following does and works synchronously:

const resolveAssetSource = require('resolveAssetSource')
let { width, height } = resolveAssetSource(imageSource)
abdennour commented 7 years ago

@jwtea solution is working with me like a charm 🎉 known that I am using flex containers

Noitidart commented 7 years ago

I gave up and had to create an Image component with onLoad to get the source image/width and then setState to set the image dimensions. I will revisit later, this is too much overhead for something so simple.

ragamufin commented 7 years ago

@Noitidart Totally agreed. The team made the decision to not mimic the concept of automatically sizing images and maintaining their aspect ratio as it is on the web. I can see the pros for that but boy it sure is a pain to get an image to be width 100% (yet not wider than it's container) while having the height automatically adjust. It's such a common use-case that it would be nice to have something for this out of the box someday

comur commented 6 years ago

For me nothing worked except this approach: https://stackoverflow.com/a/42556256 Other solutions depend top container configurations / styling

mjstelly commented 6 years ago

I agree with @Noitidart. We've been dealing with image tags since the birth of WWW. Why is it still so difficult and wonky requiring dozens of kludgy workarounds? I've tried nearly every suggestion here. So far, none of them have worked. I'm just venting. So feel free to ignore.

sparshgr8 commented 6 years ago

How should I proceed, If I have to show full width and auto height of images inside a list (flatlist/sectionlist/listview) like the Instagram app? The current Examples work well as in Individual Component only, When I put the component inside renderItem of flatlist the images don't show. <FlatList data={['a', 'b', 'c']} renderItem={({ item }) => ( <View> <Text>{item}</Text> <Image style={{ flex:1, width: null, height: null } } resizeMode="contain" source={{ uri: 'http://www.transphone.net/images/android-transfer/android-screenshot2.jpg'}} /> </View> )} />

Any Help is much Appreciated.

wassgha commented 6 years ago

Here's a module I made that does exactly this https://www.npmjs.com/package/react-native-fullwidth-image

aussig commented 6 years ago

I found that @jwtea 's solution almost worked for me (thanks Jack!), but although the image was scaled horizontally, there was excessive automatic padding (whitespace) inserted above and below the image. Adding an aspectRatio with correct values for my image solved this:

<View 
    style={{flex: 1, flexDirection: 'row'}}>
      <Image
        resizeMode='contain'
        style={{
          flex: 1,
          width: null,
          height: null,
          aspectRatio: 926/606}}
        source={Images[status]}
      />
    </View>

In my case my image was 926 wide and 606 high, so I just used those values directly. A more sensible developer might have simplified the aspect ratio of their source image first to e.g. 16/9!

For me, this works across both Android and iOS, tested on a few device sizes.

onka13 commented 6 years ago

Here is my solution;

const img = (source) => {
    const { width, height } = Image.resolveAssetSource(source)
    return <Image source={source} style={{ width: null, height: null, resizeMode: 'cover', aspectRatio: width / height }} />
}
<View style={{ flexDirection: 'row' }}>
    <View style={{ flex:2 }}>
        {img(require('./assets/images/1.png'))}
    </View>
    <View style={{ flex:1 }}>
        {img(require('./assets/images/2.png'))}
    </View>
</View>