React Native Retrieve Actual Image Sizes

React Native

React Native Problem Overview


I would like to be able to know the actual size of a network-loaded image that has been passed into <Image /> I have tried using onLayout to work out the size (as taken from here https://github.com/facebook/react-native/issues/858) but that seems to return the sanitised size after it's already been pushed through the layout engine.

I tried looking into onLoadStart, onLoad, onLoadEnd, onProgress to see if there was any other information available but cannot seem to get any of these to fire. I have declared them as follows:

  onImageLoadStart: function(e){
    console.log("onImageLoadStart");
  },

  onImageLoad: function(e){
    console.log("onImageLoad");
  },

  onImageLoadEnd: function(e){
    console.log("onImageLoadEnd");
  },


  onImageProgress: function(e){
    console.log("onImageProgress");
  },

  onImageError: function(e){
    console.log("onImageError");
  },

  render: function (e) {
    return (
      <Image
        source={{uri: "http://adomain.com/myimageurl.jpg"}}
        style={[this.props.style, this.state.style]}
        onLayout={this.onImageLayout}
        onLoadStart={(e) => {this.onImageLoadStart(e)}}
        onLoad={(e) => {this.onImageLoad(e)}}
        onLoadEnd={(e) => {this.onImageLoadEnd(e)}}
        onProgress={(e) => {this.onImageProgress(e)}}
        onError={(e) => {this.onImageError(e)}} />
    );
  }

Thanks.

React Native Solutions


Solution 1 - React Native

Image component now provides a static method to get the size of the image. For example:

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

Solution 2 - React Native

You can use resolveAssetSource method from the Image component :

import picture from 'pathToYourPicture';  
const {width, height} = Image.resolveAssetSource(picture);

Solution 3 - React Native

This answer is now out of date. See Bill's answer.

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

Old Answer (valid for older builds of react native)

Ok, I got it working. Currently this takes some modification of the React-Native installation as it's not natively supported.

I followed the tips in this thread to enabled me to do this. https://github.com/facebook/react-native/issues/494

Mainly, alter the RCTNetworkImageView.m file: add the following into setImageURL

void (^loadImageEndHandler)(UIImage *image) = ^(UIImage *image) {
  NSDictionary *event = @{
    @"target": self.reactTag,
    @"size": @{
      @"height": @(image.size.height),
      @"width": @(image.size.width)
    }
  };
  [_eventDispatcher sendInputEventWithName:@"loaded" body:event];
};

Then edit the line that handles the load completion:

[self.layer removeAnimationForKey:@"contents"];
self.layer.contentsScale = image.scale;
self.layer.contents = (__bridge id)image.CGImage;
loadEndHandler();

replace

loadEndHandler();

with

loadImageEndHandler(image);

Then in React-Native you have access to the size via the native events. data from the onLoaded function - note the documentation currently says the function is onLoad but this is incorrect. The correct functions are as follows for v0.8.0:

onLoadStart
onLoadProgress
onLoaded
onLoadError
onLoadAbort

These can be accessed like so:

onImageLoaded: function(data){
    try{
        console.log("image width:"+data.nativeEvents.size.width);
        console.log("image height:"+data.nativeEvents.size.height);
    }catch(e){
        //error
    }
},
...
render: function(){
    return (
        <View style={{width:1,height:1,overflow='hidden'}}>
            <Image source={{uri: yourImageURL}} resizeMode='contain' onLoaded={this.onImageLoaded} style={{width:5000,height:5000}} />
        </View>
    );
}

Points to note:

  • I have set a large image window and set it inside a wrapping element of 1x1px this is because the image must fit inside if you are to retrieve meaningful values.

  • The resize mode must be 'contain' to enable you to get the correct sizes, otherwise the constrained size will be reported.

  • The image sizes are scaled proportionately to the scale factor of the device, e.g. a 200200 image on an iPhone6 (not 6 plus) will be reported as 100100. I assume that this also means it will be reported as 67*67 on an iPhone6 plus but I have not tested this.

  • I have not yet got this to work for GIF files which traverse a different path on the Obj-C side of the bridge. I will update this answer once I have done that.

  • I believe there is a PR going through for this at the moment but until it is included in the core then this change will have to be made to the react-native installation every time you update/re-install.

Solution 4 - React Native

TypeScript example:

import {Image} from 'react-native';

export interface ISize {
  width: number;
  height: number;
}

function getImageSize(uri: string): Promise<ISize> {
  const success = (resolve: (value?: ISize | PromiseLike<ISize>) => void) => (width: number, height: number) => {
    resolve({
      width,
      height
    });
  };
  const error = (reject: (reason?: any) => void) => (failure: Error) => {
   reject(failure);
  };

  return new Promise<ISize>((resolve, reject) => {
    Image.getSize(uri, success(resolve), error(reject));
  });
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMoss PalmerView Question on Stackoverflow
Solution 1 - React NativeBillView Answer on Stackoverflow
Solution 2 - React Nativebalti bahaeddineView Answer on Stackoverflow
Solution 3 - React NativeMoss PalmerView Answer on Stackoverflow
Solution 4 - React NativeIhor KucherenkoView Answer on Stackoverflow