Images

Static Image Resources

As of 0.14 release, React Native provides a unified way of managing images in your iOS and Android apps. To add a static image to your app, place it somewhere in your source code tree and reference it like this:

<Image source={require('./my-icon.png')} />

The image name is resolved the same way JS modules are resolved. In the example above the packager will look for my-icon.png in the same folder as the component that requires it. Also if you have my-icon.ios.png and my-icon.android.png, the packager will pick the file depending on the platform you are running on.

You can also use @2x, @3x, etc. suffix in the file name to provide images for different screen densities. For example, if you have the following file structure:

.
├── button.js
└── img
    ├── check@2x.png
    └── check@3x.png

And button.js code contains

<Image source={require('./img/check.png')} />

Packager will bundle and serve the image corresponding to device's screen density, e.g. on iPhone 5s check@2x.png will be used, on Nexus 5 – check@3x.png. If there is no image matching the screen density, the closest best option will be selected.

On Windows, you might need to restart the packager if you add new images to your project.

Here are some benefits that you get:

  1. Same system on iOS and Android.
  2. Images live in the same folder as your JS code. Components are self-contained.
  3. No global namespace, i.e. you don't have worry about name collisions.
  4. Only the images that are actually used will be packaged into your app.
  5. Adding and changing images doesn't require app recompilation, just refresh the simulator as you normally do.
  6. The packager knows the image dimensions, no need to duplicate it in the code.
  7. Images can be distributed via npm packages.

Note that in order for this to work, the image name in require has to be known statically.

// GOOD
<Image source={require('./my-icon.png')} />

// BAD
var icon = this.props.active ? 'my-icon-active' : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />

// GOOD
var icon = this.props.active ? require('./my-icon-active.png') : require('./my-icon-inactive.png');
<Image source={icon} />

Note that image sources required this way include size (width, height) info for the Image. If you need to scale the image dynamically (i.e. via flex), you may need to manually set { width: undefined, height: undefined } on the style attribute.

Available React Native 0.14+. If you've generated your project with 0.13 or earlier, read this. The new asset system relies on build hooks for Xcode and Gradle that are included in new projects generated with react-native init. If you generated your projects before that, you'll have to manually add them to your projects to use the new images asset system. See Upgrading for instructions on how to do this.

Images From Hybrid App's Resources

If you are building a hybrid app (some UIs in React Native, some UIs in platform code) you can still use images that are already bundled into the app (via Xcode asset catalogs or Android drawable folder):

<Image source={{uri: 'app_icon'}} style={{width: 40, height: 40}} />

Note that this approach provides no safety checks. It's up to you to guarantee that those images are available in the application. Also you have to specify image dimensions manually.

Network Images

Many of the images you will display in your app will not be available at compile time, or you will want to load some dynamically to keep the binary size down. Unlike with static resources, you will need to manually specify the dimensions of your image.

// GOOD
<Image source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}
       style={{width: 400, height: 400}} />

// BAD
<Image source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}} />

Local Filesystem Images

See CameraRoll for an example of using local resources that are outside of Images.xcassets.

Best Camera Roll Image

iOS saves multiple sizes for the same image in your Camera Roll, it is very important to pick the one that's as close as possible for performance reasons. You wouldn't want to use the full quality 3264x2448 image as source when displaying a 200x200 thumbnail. If there's an exact match, React Native will pick it, otherwise it's going to use the first one that's at least 50% bigger in order to avoid blur when resizing from a close size. All of this is done by default so you don't have to worry about writing the tedious (and error prone) code to do it yourself.

Why Not Automatically Size Everything?

In the browser if you don't give a size to an image, the browser is going to render a 0x0 element, download the image, and then render the image based with the correct size. The big issue with this behavior is that your UI is going to jump all around as images load, this makes for a very bad user experience.

In React Native this behavior is intentionally not implemented. It is more work for the developer to know the dimensions (or aspect ratio) of the remote image in advance, but we believe that it leads to a better user experience. Static images loaded from the app bundle via the require('./my-icon.png') syntax can be automatically sized because their dimensions are available immediately at the time of mounting.

For example, the result of require('./my-icon.png') might be:

{"__packager_asset":true,"path":"/Users/react/HelloWorld/my-icon.png","uri":"my-icon.png","width":591,"height":573}

Source as an object

In React Native, one interesting decision is that the src attribute is named source and doesn't take a string but an object with a uri attribute.

<Image source={{uri: 'something.jpg'}} />

On the infrastructure side, the reason is that it allows us to attach metadata to this object. For example if you are using require('./my-icon.png'), then we add information about its actual location and size (don't rely on this fact, it might change in the future!). This is also future proofing, for example we may want to support sprites at some point, instead of outputting {uri: ...}, we can output {uri: ..., crop: {left: 10, top: 50, width: 20, height: 40}} and transparently support spriting on all the existing call sites.

On the user side, this lets you annotate the object with useful attributes such as the dimension of the image in order to compute the size it's going to be displayed in. Feel free to use it as your data structure to store more information about your image.

Background Image via Nesting

A common feature request from developers familiar with the web is background-image. To handle this use case, simply create a normal <Image> component and add whatever children to it you would like to layer on top of it.

return (
  <Image source={...}>
    <Text>Inside</Text>
  </Image>
);

Off-thread Decoding

Image decoding can take more than a frame-worth of time. This is one of the major source of frame drops on the web because decoding is done in the main thread. In React Native, image decoding is done in a different thread. In practice, you already need to handle the case when the image is not downloaded yet, so displaying the placeholder for a few more frames while it is decoding does not require any code change.

doc_React_Native
2016-06-23 04:24:10
Comments
Leave a Comment

Please login to continue.