I'm trying to use a ScrollView (with paging enabled) in React Native to page through a series of images. Anyone know how to make the image views fill each page of the scroll view? So far I've only had luck hard coding width and height values for the image style.
Here's roughly what I'm doing:
render: function() {
return (
var images = [{ url: 'http://url/to/image.jpg' }, { url: 'http://url/to/another-image.jpg'}];
<ScrollView horizontal={true} pagingEnabled={true} style={styles.myScrollViewStyle}>
{images.map(image => {
return (
<Image source={{uri: image.url}} style={styles.myImageStyle} />
);
})}
</ScrollView>
);
}
The only way images show up is if I hardcode a width/height number in the style. I've been unable to get the Image to just flex to fill 1 whole page.
ScrollView style:
scrollView: {
flex: 1,
backgroundColor: '#000000',
}
Image style:
image: {
width:375,
height:667,
flex: 1,
backgroundColor: 'rgba(0,0,0,0)',
}
To set image dimensions use next code:
var Dimensions = require('Dimensions');
var windowSize = Dimensions.get('window');
...
image: {
width: windowSize.width,
height: windowSize.height,
flex: 1,
backgroundColor: 'rgba(0,0,0,0)',
}
I do not believe it is possible to achieve this using only Flex right now, as it is going to try to contain all the images within your container.
ScrollView does have a contentContainerStyle={} property however, so I could envision a solution being something like setting the width of the container to be (window.width * number of items) which would then allow flex:1 on each image child do what you expect.
Unfortunately there is currently no way to fetch the window width (yet) https://github.com/facebook/react-native/pull/418
At the moment it seems like hard coding dimensions is the only option.
Related
I wonder how to use mui's theme.mixins.toolbar to calculate height: calc(100vh - toolbar)?
I am currently trying to do
function SwipeCard() {
return (
<Box
sx={{
height: (theme) => `calc(100vh - ${theme.mixins.toolbar.minHeight}px)`,
paddingTop: (theme) => theme.mixins.toolbar.minHeight + "px",
}}
>
hello world
</Box>
);
}
export default SwipeCard;
But when I change viewport size and the toolbar becomes bigger. Theme.mixins.toolbar.minHeight stays the same at 56 instead of expected 64?
So I found out you can just add a second empty Toolbar to act as the padding for your content if your main Toolbar position is fixed. It comes automatically with the same media queries as the main Toolbar.
If you make this {...theme.mixins.toolbar} you will get the minHeight responsively.
So I used this in Box component that I placed above to simulate paddingTop.
I have a line on the middle of my screen, that works with Accelerometer and moves this top or bottom and also supposed to rotate. I am using LayoutAnimation, but seems like this method doesn't allow me to rotate my line smoothly, it goes well with 'top' property, but it's not working with rotation
I've tried to install react-native-canvas, but this package wrecks my app at all so I have to recreate it ;D (it pissed me off).
Also I tried to make this animation with interpolate, but it seems like working for fixed degrees and looks weird
componentDidMount(){
setUpdateIntervalForType(SensorTypes.accelerometer, 150);
const subscription = accelerometer.subscribe(({ x, y, z }) =>{
let d = getAngles(x,y,z);
this.updateValues(d.roll,d.pitch);
}
);
}
updateValues(roll,pitch){
LayoutAnimation.configureNext(CustomLayoutAnimation)
this.setState({roll,pitch})
}
render() {
return (
...
{<View style={{position:"relative",top:calculateOffset(this.state.roll,this.state.screenOffset)+"%",width:"80%",height:4,backgroundColor:"orange",transform:[{rotate:`${this.state.pitch}deg`}]}} />}
...
);
}
I'm on search for some working package to work with or way to solve this problem.
Try this:
<View
style={{
position:"relative",
top: calculateOffset(this.state.roll,this.state.screenOffset)+"%",
width: "80%",
height: "80%",
backgroundColor: "transparent",
justifyContent: "center",
transform:[{rotate:`${this.state.pitch}deg`}]}}
}}
>
<View
style={{
height: 4,
backgroundColor: "orange",
width: "100%"
}}
/>
</View>
I hope it help you.
I resolved this with interpolation and Animated API.
It looks weird though, but works ;)
I have a panel in which the keyboard is always up since I do not wish the users to dismiss it. In that panel I have a FlatList which looks like this:
<KeyboardAvoidingView style={{ flex: 1 }}>
<FlatList
// This keeps the keyboard up and disables the user's ability to hide it.
keyboardShouldPersistTaps="handled"
data={this.state.examples}
keyExtractor={(item, index) => index.toString()}
renderItem={this._renderItem}
contentContainerStyle={{ flex: 1}}
/>
</KeyboardAvoidingView>
So far so good, I have achieved what I wanted. However, when the keyboard is up - it hides the bottom part of the items rendered by the FlatList. And users cannot scroll up and view the last items because they stay behind the keyboard.
How can I preserve the Keyboard opened (and disable the ability to be dismissed) whilst being able to view and scroll through the whole content of the FlatList?
You can add a keyboard listener event to get the height of the keyboard.
this.keyboardWillShowListener = Keyboard.addListener('keyboardWillShow', (e) => {
this.setState({ keyboardHeight: e.endCoordinates.height, keyboardShow: true })
Animated.timing(this.visibleHeight, {
duration: e.duration,
toValue: 1,
easing: Easing.inOut(Easing.ease)
}).start()
})
View code like this
<Animated.View style={Platform.OS === 'android' ? { flex: 1 } : {
height: this.visibleHeight.interpolate({
inputRange: [0, 1],
outputRange: [height - this.NavHeaderHeight, height - this.state.keyboardHeight - this.NavHeaderHeight]
})
}
} >
/*Your FlatList*/
</Animated.View>
I hope it works for you
I've been to a similar situation. I had a bottom Floating Action Button at the lower right corner, hiding the last item a bit.
So, I added a fake blank item to the end of the list so that I could scroll it up a bit more.
It's simple and tricky. I hope it works for you as well, if you add a few blank itens or one wide enough blank item.
EDIT 1:
Suppose your data array is something like this: [{title: "Item 1"}, {title: "Item 2"}]
You have to concat the new blank item to the data array while passing it to the <FlatList>, like this:
<FlatList
keyboardShouldPersistTaps="handled"
data={this.state.examples.concat({title:"\n\n\n\n\n"})}
keyExtractor={(item, index) => index.toString()}
renderItem={this._renderItem}
contentContainerStyle={{ flex: 1}}/>
Adjust the amount of "\n" until you can scroll the list to be visible. There must be a minimum amount. And make sure your _renderItem don't set the item hight to a fixed value.
I'm building an availability calendar in our app and for days that have check-ins and check-outs (which happen at 12:00PM) I wish to show the calendar day either half green or half right, like this:
With CSS, currently, we achieve this effect like this:
linear-gradient(to bottom right, #beffbe 50%, #ffbdc2 51%)
What would be the best way of implementing this?
I'm using expo so maybe something involving <LinearGradient> ?
Don't know if this is the best solution, but you can achieve this effect by adding a bordered <View /> as a sibling of your content:
import { Dimensions } from 'react-native';
const { width } = Dimensions.get('window');
render() {
(...)
<View style={{
borderRightWidth: width,
borderRightColor: 'red',
borderTopWidth: width,
borderTopColor: 'green',
position: 'absolute',
opacity: 0.5
}} />
[Your content goes here]
(...)
}
Hope it helps
I want to put content (multiple images vertically arranged) in a React Native ScrollView (iOS only for now, Android will come later) that is bigger than the phone's screen, and start zoomed out so that it is all visible at the same time.
Are there any good examples of using ScrollView.scrollResponderZoomTo in a componentDidMount call that zooms out to fit content in the screen, something like
<ScrollView
style={{width: 500, height: 1000}}
// + whatever other properties make this work required way
>
<View style={{width: 2000, height: 5000}}>
<Image style={{width: 2000, height: 2000}} source={.....}/>
<Image style={{width: 2000, height: 3000}} source={.....}/>
</View>
</ScrollView>
I tried setting the 'zoomScale' property, but that seems to be ignored and always uses the value 1.
According to this issue (https://github.com/facebook/react-native/issues/2176) there is a scrollResponderZoomTo function that can be used, but when I try to use it, it seems that no matter what values I give it it zooms out much too far and off center.
The F8 sample app has a ZoomableImage module (https://github.com/fbsamples/f8app/blob/b5df451259897d1838933f01ad4596784325c2ad/js/tabs/maps/ZoomableImage.js) which uses the Image.resizeMode.contain style to make an image fit the screen, but that loses the quality of image, so when you zoom in it gets blurry.
This may not be the way you intended to do this, but a possible solution:
You may get the devices height and width (var {height, width} = Dimensions.get('window')) and you know your image sizes,so you may easily calculate the needed width and height, let's call them var neededWidth, neededHeight;. You may then calculate the zoom to which you would like to zoom out: var zoom = Math.min(height / neededHeight, width / neededWidth);.
With these values in place, you may set an Animated value for the zoom, starting at 1 ending at zoom like this in your componentWillMount:
Animated.timing(
this.state.animatedZoom,
{toValue: zoom}
).start();
The constructor would look like this:
constructor(props) {
super(props);
this.state = {
animatedZoom: new Animated.Value(1),
};
}
The render function would look like this (reference for transform can be found here):
<ScrollView
style={{width: 500, height: 1000, transform: [{ scale: this.state.animatedZoom }]}}
>
<View style={{width: 2000, height: 5000}}>
<Image style={{width: 2000, height: 2000}} source={.....}/>
<Image style={{width: 2000, height: 3000}} source={.....}/>
</View>
</ScrollView>
I found a way to control the zoom programatically. Let's say you want to set the default zoom level when the scrollview mounts. You can use the method scrollResponderZoomTo of the ScrollView's scroll responder with a timeout.
setScrollViewRef = (ref) => {
this.mapScrollView = ref;
setTimeout(() => {
this.mapScrollView._scrollResponder.scrollResponderZoomTo({
height: this.mapSize, width: this.mapSize, animated: false,
});
}, 1);
};
And render your scrollview with a contentContainerStyle constraining the size you want.
renderMapView = () => {
this.mapSize = Dimensions.get('window').width * 2;
return (
<ScrollView
style={{ flex: 1 }}
ref={this.setScrollViewRef}
maximumZoomScale={4}
minimumZoomScale={0.5}
contentContainerStyle={{ width: this.mapSize, height: this.mapSize }}
centerContent
>
<Map width={this.mapSize} height={this.mapSize} />
</ScrollView>
);
};
This will set the default zoom level to 50% and let the whole <Map /> be visible. If you want another zoom level, you can provide a different height/width to scrollResponderZoomTo. Only works on iOS.
#Levalis's answer save my day.
scrollResponderZoomTo is currently the best shot in React Native according to this issue #2176
If anyone encounter the "zooms out much too far and off center" problem, try to call scrollResponderZoomTo with the same rect as you set in your style:
// declare image size in styles.js...
image: {
width: <image width>,
height: <image height>
}
// ...and inside your component
this.scrollView._scrollResponder.scrollResponderZoomTo({
height: <image height>, width: <image width>, animated: false,
})