Overlay button on top of Image in React Native

Viewed 44485

I'm trying to achieve the following effect in React Native:

image with an icon in the corner

The image has a button in the corner. The button is always within the corner of the image regardless of the image's size or aspect ratio, and no part of the image is clipped (it is always scaled down to fit fully within a box).

The trouble I'm having in React Native is that the Image component's size doesn't always match the scaled-down size of the image. If I fix the image's height to 300, set flex 1 to make the image's width expand to fill its contents, and the image is a portrait, the Image component with being the full width of the container, but the image within the component will have a width of much less. Therefore, the typical approach for having a view overlay another view doesn't work as I would like it to- my overlay also covers the padding around the image, and the button (anchored to the corner) appears outside of the image.

Here's what it looks like in React Native:

portrait image with button overlay in React Native

The X is a placeholder for the button. It is set to anchor to the top-left of a View that's a child of the same View that the Image is a child of. The backgroundColor of the image is set to green to demonstrate how the width of the Image component is different from the width of the picture that's inside the component.

The goal is that the X would be inside of the image regardless of its aspect ratio. I think I could do something based on grabbing the image's dimension and scaling the height and width of the Image component, but that sounds complicated and fragile. Is this possible in a responsive way with styling?

Demonstration code:

   <View
      style={{
        marginLeft: 7,
        marginRight: 7,
        backgroundColor: 'blue',
      }}
    >
      <View
        style={{
          height: 300,
          flex: 1,
        }}
      >
        <Image
          source={imageSource}
          style={{
            flex: 1,
            height: undefined,
            width: undefined,
            backgroundColor: 'green',
          }}
          resizeMode="contain"
        />
      </View>
      <View
        style={{
          position: 'absolute',
          right: 5,
          top: 5,
          backgroundColor: 'transparent',
        }}
      >
        <Text style={{ color: 'white' }}>X</Text>
      </View>
    </View>
4 Answers

Here is how I accomplished that:

import React, { Component } from "react";
import { View, Image, StyleSheet } from "react-native";
import { Ionicons } from "@expo/vector-icons";

class MyCard extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Image
          resizeMode="cover"
          style={styles.cover}
          source={{ uri: "https://picsum.photos/700" }}
        />
        <Ionicons style={styles.close} name="ios-close-circle" size={25} />
      </View>
    );
  }
}

export default MyCard;

const styles = StyleSheet.create({
  container: {
    margin: 5,
    width: 160,
    height: 200
  },
  cover: {
    flex: 1,
    borderRadius: 5
  },
  close: {
    margin: 5,
    position: "absolute",
    top: 0,
    left: 0,
    width: 25,
    height: 25,
    color: "tomato"
  }
});

Here is how it looks like :

enter image description here

Updated 29 Jun 2019

Now(react-native@0.60-RC) there's a specific component to wrap elements as image background, ImageBackground. Go for the official documentation.

Following Gist has been changed.


Original Answer

We can use <Image/> display images, and we can use it as a background-image hack.

try this

<Image
  source={imageSource}
>
  <View>
    <Text>×</Text>
  </View>
</Image>

this gist is a full demo for your need.

or you can see it live at expo:

<div data-snack-id="B1SsJ7m2b" data-snack-platform="ios" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#fafafa;border:1px solid rgba(0,0,0,.16);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.io/embed.js"></script>

Related