8

Even though I can get a screen orientation change to update state and re-render, the ImageBackground component still will not update its width.

I have the following code:

<View
  onLayout={event => this.mylayoutchange(event)}
  style={{ flex: 1, backgroundColor: 'green' }}
>
  <ImageBackground
    style={{ flex: 1, width: this.state.width, height: this.state.height }}
    imageStyle={{ resizeMode: 'repeat' }}
    source={require('../assets/images/my_background.jpg')}
  >
    <View>
      <Text>.... Other code here....</Text>
    </View>
  </ImageBackground>
</View>;

When the user changes the orientation of the device the mylayoutchange() function gets called. It updates state correctly. The render function will update. Width and height are correctly changed as can be seen in console.log(). However, for whatever reason, the <ImageBackground> does not update its correct width and height.

When the screen is rotated, the background image no longer fills the entire size of the screen and I instead see the green background color.

What am I doing wrong?

My environment:

"react": "16.13.1",
"react-native": "0.63.2",
diedu
  • 19,277
  • 4
  • 32
  • 49
kojow7
  • 10,308
  • 17
  • 80
  • 135
  • If it is possible for you, please share a re-production of your issue on Snack. surly, we can help better. –  Oct 20 '20 at 22:59

3 Answers3

5

You need to use resize for resizeMethod in order for the image to get actual resize:

resizeMethod

The mechanism that should be used to resize the image when the image's dimensions differ from the image view's dimensions. Defaults to auto.

  • auto: Use heuristics to pick between resize and scale.

  • resize: A software operation which changes the encoded image in memory before it gets decoded. This should be used instead of scale when the image is much larger than the view.

  • scale: The image gets drawn downscaled or upscaled. Compared to resize, scale is faster (usually hardware accelerated) and produces higher quality images. This should be used if the image is smaller than the view. It should also be used if the image is slightly bigger than the view.

It's obvious that RN picks scale in your case, thus you have to explicitly set it to resize for it to work when the orientation changes and flex style change kicks in:

return (
  <View
    style={{ flex: 1, backgroundColor: 'blue' }}
  >
    <ImageBackground
      // All props other than children, style, imageStyle, imageRef,
      // will be passed to the inner Image component,
      // so, we can use resizeMethod that will be passed to the Image component

      resizeMethod="resize"

      style={{
        flex: 1,
        borderWidth: 1,
        borderColor: 'red'
      }}
      imageStyle={{
        resizeMode: 'repeat',
      }}
      source={require('../assets/images/my_background.jpg')}
    >
      <View>
        <Text style={{
          backgroundColor: 'white'
        }}>.... Other code here....</Text>
      </View>
      <View style={{
        position: 'absolute',
        right: 0,
        bottom: 0
      }}>
        <Text style={{
          backgroundColor: 'white'
        }}>Absolute bottom-right text</Text>
      </View>
    </ImageBackground>
  </View>
);

Result screen capture:

Screen capture

Tested with Environment:

"react": "16.13.1",
"react-native": "0.63.2",

Sample tile image used for repeat background (400 X 400 pixels):

Background tile repeat image

Expo Snack:

RN ImageBackground Orientation Change

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
  • Hmmm, it does work. It's strange that it says `scale` should be used if the image is smaller than the view. My image is smaller than the view so it is counterintuitive that I should be using `resize` instead. – kojow7 Oct 28 '20 at 03:21
  • @kojow7 you're trying to use this property in your existing code that uses `width` and `height` calculated inside `onLayout` and most likely you're using spread syntax to apply the props `{...dimensionsSize}` in `ImageBackground` style which of couse it won't work because `Dimentions` helper class object also returns `fontScale` and `scale` along with `width` and `height` (screenshot: https://prnt.sc/v8ivzq) which `fontScale` and `scale` are not valid style properties. Please pay attention to my code as I do not use `onLayout` at all. – Christos Lytras Oct 28 '20 at 09:51
  • @kojow7 you say it's *"counterintuitive"* but RN as any piece of software is not perfect and that's why you're here trying to figure this issue out. The fact is that the image won't render over the original portrait size if it won't have `resize` for `resizeMethod` for it to have a proper `resizeMode: "repeat"` drawing. Good luck finding a better solution. – Christos Lytras Oct 28 '20 at 09:52
  • @ChristosLytras I've also tried using width: '100% and height: '100%' and still had the same issue. I also think you are misunderstanding me. By 'counterintuitive' I was saying that the documentation does not seem to make sense with what we are actually seeing. I did not say I was trying to find a better solution. Your solution works just fine so that's what I am using. – kojow7 Oct 28 '20 at 15:12
  • @kojow7 yes it seems I've misunderstood it. It's not only the documentation but also the *"decision"* that RN makes with the `auto` `resizeMethod` which it sets to `scale` of course because the image source is smaller than the view. I think it's an issue but I'm not sure (maybe it's a [Fresco](https://frescolib.org/) issue, did you encounter the same when running on iOS? I've only tested it using Android emulator). I'm not sure also how this can impact the performance especially when using on older devices. – Christos Lytras Oct 28 '20 at 15:19
  • 1
    You surprise me with your awesome answer. like always. That's the point. – AmerllicA Oct 31 '20 at 09:19
0

You can specify the width and height automatically using just CSS, this will ensure that your View takes the full width/height for every size/layout.

     <View
        onLayout={(event) => this.mylayoutchange(event)}
        style={{flex: 1, backgroundColor: 'green'}}>
        <ImageBackground
          style={{
            flex: 1,
            width: '100%',
            height: '100%',
          }}
          imageStyle={{resizeMode: 'repeat'}}
          source={require('../assets/images/my_background.jpg')}>
          <View>
            <Text>.... Other code here....</Text>
          </View>
        </ImageBackground>
      </View>

Considerations:

  • There's a bug in react native that may still exist in your RN version you can take a look to the issue
  • If you still need to specify the width and height using this.state.width and this.state.height consider using usewindowdimensions it is more suitable because it updates automatically.
Oussama Bouthouri
  • 615
  • 2
  • 8
  • 23
  • I originally had it at 100% and it was still giving me the same issue when rotated. This is why I switched to a state-based option as I figured the re-render would update it. It still does not. – kojow7 Oct 20 '20 at 16:03
  • @kojow7 the bug mentioned in this answer is definitely the reason you're having the problem, since it got closed you can open a new issue in github so this get attention again – diedu Oct 20 '20 at 16:09
0

I just ran into a similar issue. One workaround that I found is to generate a unique ID (UUID) on each orientation change, and use that UUID as a key for the ImageBackground. This will remount the image and work around the bug.

EmpireJones
  • 2,936
  • 4
  • 29
  • 43