ascoders / react-native-image-viewer

🚀 tiny & fast lib for react native image viewer pan and zoom
MIT License
2.44k stars 577 forks source link

renderHeader messing up with renderFooter #401

Open micheilsgrey opened 4 years ago

micheilsgrey commented 4 years ago

I noticed that if I use renderHeader and renderFooter together, my footer needs to add the exact height of the header otherwise it will be behind the screen on the bottom. If the height in my header is 50 so with a height of 70 of my footer it's seen as 20 pixels.

_renderHeader = (currentIndex: number, pics: IImageInfo[]) => {
    return (
      <View style={{ height: 50, backgroundColor: 'transparent' }}>
        <Text style={{ color: 'white' }}>{`${currentIndex + 1}/${
          pics.length
        }`}</Text>
      </View>
    );
  };
  _renderFooter(currentIndex: number) {
    return (
      <View style={{ height: 70, backgroundColor: 'red' }}> //i see only 20 pixels here since header is 50 
        <Text style={{ fontSize: 16, color: 'white', textAlign: 'center' }}>
          {currentIndex + 1}
        </Text>
      </View>
    );
  }
VictorAssis commented 3 years ago

If I understand correctly, the header is not absolute positioned as a footer. Therefore, the header pushes down the image area, making the footer disappear.

In my case, I also force the absolute position in the header and both appear:

const Lightbox = ({ images, selected, onCloseLightbox }: Props) => {
  const renderHeader = () => (
    <View style={styles.header}>
      <Text style={{ color: '#fff' }}>Header</Text>
    </View>
  )

  const renderFooter = (index: number) => (
    <Text style={styles.footerText}>{index + 1} / {images.length}</Text>
  )

  return (
    <Modal
      transparent={true}
      animationType="fade"
      visible={typeof selected !== 'undefined'}
      onRequestClose={onCloseLightbox}
    >
      <ImageViewer
        imageUrls={images} 
        index={selected}
        enableSwipeDown
        onSwipeDown={onCloseLightbox}
        renderHeader={renderHeader}
        renderFooter={renderFooter}
        footerContainerStyle={styles.footer}
        renderIndicator={() => <React.Fragment />}
      />
    </Modal>
  );
}

const styles = StyleSheet.create({
  header: {
    position: 'absolute',
    top: 0,
    left: 0,
    backgroundColor: '#000',
    width: '100%',
    zIndex: 9999
  },
  footer: {
    width: '100%',
    backgroundColor: '#000'
  },
  footerText: {
    color: '#fff',
    textAlign: 'center',
    margin: 10
  }
})
micheilsgrey commented 3 years ago

I will try do as in your example. Was thinking that's a bug, thanks .

VictorAssis commented 3 years ago

My solution was incomplete. Its work when has no problem that header and footer can overlap the image (but isn't my case).

To exemplify, here is a print when image is vertically and hide behind header and footer:

The solution is place header before the <ImageViewer> and footer after. This way, the view with the image gonna calculate the height considering the spacing between header and footer:

const Lightbox = ({ images, selected, onCloseLightbox }: Props) => {
  const [index, setIndex] = useState<number>()
  const insets = useSafeAreaInsets();

  const renderHeader = () => (
    <View
      style={[
        styles.header,
        Platform.OS === 'ios' ? { paddingTop: insets.top } : { }
      ]}
    >
      <MaterialIcons
        name='close'
        style={styles.close}
        onPress={onCloseLightbox}
      />
    </View>
  )

  const renderFooter = () => (
    <View style={[
      styles.footer,
      Platform.OS === 'ios' ? { paddingBottom: insets.bottom } : { }
    ]}>
      <CustomText
        weight='600'
        style={styles.footerText}
      >{index || 1} / {images.length}</CustomText>
    </View>
  )

  return (
    <Modal
      transparent
      animationType="fade"
      visible={typeof selected !== 'undefined'}
      onRequestClose={onCloseLightbox}
    >
      {renderHeader()}
      <ImageViewer
        imageUrls={images} 
        index={selected}
        enableSwipeDown
        enablePreload
        onSwipeDown={onCloseLightbox}
        renderIndicator={(index?: number) => {
          setIndex(index)
          return <React.Fragment />
        }}
        useNativeDriver
      />
      {renderFooter()}
    </Modal>
  );
}

const styles = StyleSheet.create({
  header: {
    backgroundColor: '#000',
    width: '100%',
    zIndex: 9999
  },
  close: {
    alignSelf: 'flex-end',
    color: '#fff',
    fontSize: 30,
    margin: 10
  },
  footer: {
    width: '100%',
    backgroundColor: '#000'
  },
  footerText: {
    color: '#fff',
    textAlign: 'center',
    margin: 10
  },
  arrow: {
    color: '#fff',
    fontSize: 40,
    elevation: 2,
    backgroundColor: 'rgba(0, 0, 0, 0.3)'
  }
})

export default Lightbox;