React Native : Subviews of KeyboardAvoidingView have lag / move at different speeds - javascript

I would like them all the subviews to move in one clean motion, but if you look at the gif, you can see that the Text subview and the TextInput subview overlap and move at different speeds. It looks like the Text subview adjusts its position instantly where as the button and TextInput subviews adjust their position in more of an Ease in Ease out manner.
Main exported component
class SearchScreen extends React.Component {
state = {search:""}
render(){
getArguments = {
search: this.state.search
}
return (
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<Text style={styles.searchTitle}>Search for a Movie in the OMDB Database</Text>
<TextInput style={styles.searchField} onChangeText={text => this.setState({search:text})} ></TextInput>
<SearchButton navigation = {this.props.navigation} getArguments={getArguments}/>
</KeyboardAvoidingView>
)
}
}
Styling
const styles = StyleSheet.create({
container: {
flex:1,
backgroundColor: '#C8FEFE',
alignItems: 'center',
justifyContent: 'center'
},
searchButton: {
marginTop: 20,
backgroundColor: '#24D9E8',
borderRadius: 5,
padding: 5
},
searchField: {
backgroundColor: '#FFF',
textAlign: 'center',
width: 200,
borderRadius: 5,
margin: 20,
height: 30
},
searchTitle:{
fontWeight: 'bold',
fontSize: 20,
textAlign:'center'
}
});
Github
Full project on github

Solution 1: Quick fix for iOS
You can wrap your elements in a View, which will make them react to keyboard the way you want:
// styles
contentContainer: {
alignItems: 'center',
}
// SearchScreen
<View style={styles.contentContainer}>
<Text style={styles.searchTitle}>Search for a Movie in the OMDB Database</Text>
<TextInput style={styles.searchField} onChangeText={text => this.setState({search:text})} ></TextInput>
<SearchButton navigation = {this.props.navigation} getArguments={getArguments}/>
</View>
However, this will only work on iOS. Keyboard works slightly differently on Android. So solution 2 is a more solid way to do things.
Solution 2: Animations
Keyboard avoidance is quite tricky, Spencer Carli's article that Dominik referred to is a great resource for solutions using KeyboardAvoidingView. Those usually give you what you need. But if you want a smooth, controlled transition between keyboard states, you should use animations. The flow would go like this:
Add Keyboard event listeners to your component (keyboardDidShow and keyboardDidHide)
Wrap the content you want to move in an Animated.View
on keyboardDidShow, animate the y position of the Animated.View by the offset you want. The event returned (of type KeyboardEvent), along with the Dimensions API have all the measurements you need. Tip: use an Animated.timing animation with bezier Easing to control how the view moves.
on keyboardDidHide, repeat the animation but in the opposite direction.
Hope this helps!

<KeyboardAvoidingView keyboardVerticalOffset={Platform.OS === 'ios' ? 40 : 0}>
...
<KeyboardAvoidingView/>
You can try adding some offset to the view.

I would definitely style it with flexbox, try styling your container like this:
(it should move them at once, as flexbox will fit them on current width and height)
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;

Related

How to add smooth transitions to the React Native elements

I know there is Animated API for React Native but as far as I've seen it's very complicated than what I want to implement in this particular situation.
Now I have a button that has a changing text state inside while scrolling to the page. The texts' length differ so button has to stretch or shrink. But of course this happens instantly, I want it to stretch or shrink smoothly. In web, we could easily do something like this in CSS ;
transition: all 0.3s ease;
When you don't want to add extra animations this can be enough. So what is the equvialent of this behaviour in React Native?
The equivalent to CSS transitions is LayoutAnimation from React Native. Before you make a state change regarding the button, you configure the LayoutAnimation. Then the next state update will be animated at that position. This can look like this:
export default function App() {
const [text, setText] = React.useState("This is an example");
React.useEffect(() => {
setTimeout(() => {
LayoutAnimation.spring();
setText("This is an another example");
}, 1500);
}, [])
return (
<View style={styles.container}>
<View style={{backgroundColor: 'red', padding: 20}}>
<Text>{text}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
}
});
Prerequisite is always that it goes via a state change. Just play around with a sample View Box and give the width and height over the useState hook. If you configure your LayoutAnimation before you write setWidth, then the component with the state will be animated automatically.

Problems with TouchableOpacity not working with position:"absolute"

first time posting.
I am creating a kind of popup menu to select a color for a draggable object.
I've created the components and it's all working fine, except for the fact that I can't click the buttons, or nothing happens when I do.
at the moment I am using the package: 'react-native-easy-gestures' as I was too time-consuming to create the panhandlers myself for this project
Because I am using draggable components, the half circle of colors are positioned around a view that is set to position: 'absolute', and I think that's the problem.
This is my style for the popover component (plus i dynamically set the top and left positions when mapping through the colors.
and there is of course a higher leveled style for the actual "item" that I can drag which is all placed inside the Gesture component from the node modules mentioned above.
button: {
height: '100%',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
zIndex: 6,
},
buttonView: {
position: 'absolute',
zIndex: 5,
},
popoutMenu: {
position: 'absolute',
alignSelf: 'center',
alignContent: 'center',
justifyContent: 'center',
},
Screenshot of the menu and item
I do believe that the problems lies in how absolute is working. I have the buttons inside their own view at the moment, but I also tried by just having them alone, still, nothing :/
Any help would be much appreciated
So I figured it out eventually. First I changed to using the TouchableOpacity from 'react-native-gesture-handler' and then I created a View inside of this touchable that has the dimensions (width and height) of the "button" and it seems to work quite well now.
The problem was as far as I can understand that the touchableXs usually take the style components of their child, so since I didn't really have any children inside of it, the button was just not there.
I also used the one from a different package as it allowed me to set the zIndex of the button without affecting if it is clickable.
At the moment I placed an Icon inside of it, but I am considering removing it and just creating a circle from the view with borderRadius.
Above here is a map function that goes through all my colors from an array.
<View
key={color}
style={[
styles.button,
{
top: coords.y - buttonSize / 2,
right: coords.x - buttonSize / 2,
height: buttonSize,
width: buttonSize,
padding: 5,
borderRadius: buttonSize,
// backgroundColor: 'red',
},
]}>
<TouchableOpacity
color={color}
onPress={() => {
!isExitButton
? !isResetButton
? setTintColor(color)
: setTintColor(null)
: setPopoutActive(false);
}}>
<View
style={[
styles.circleInTouchable,
{
width: buttonSize,
height: buttonSize,
},
]}>
<Icon
name={'circle'}
solid
size={buttonSize}
color={color}
/>
</View>
</TouchableOpacity>
</View>
Import TouchableOpacity from react-native-gesture-handler instead react-native.
import { TouchableOpacity} from 'react-native-gesture-handler'

Image in custom header with react-navigation is flickering / blinking on navigating

I have my own HeaderComponent with an image:
const CustomHeaderLogo = () => {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Image
source={require("../assets/logo.png")}
style={{ width: 160, height: 30 }}
/>
</View>
);
};
export default CustomHeaderLogo;
which I use in navigationOptions in stackNavigator on all my screens like this:
navigationOptions: {
headerTitle: () => <HeaderLogo />
}
It works but the problem is, that the image is flickering / blinking when navigating from screen to screen. That's especially noticeable on iOS.
Is there any way to prevent the image from flickering?
EDIT: Need to edit my question since I now know where the flickering is coming from.
It is the animation effect in the header. I still don't know how to disable it since animationEnabled setting to false doesn't change anything. https://reactnavigation.org/docs/4.x/stack-navigator#animationenabled

How to put a sticky Touchable within an horizontal ScrollView in React Native?

I am trying to make a simple application/game to learn and practice React Native. Currently, I am trying to create a sticky TouchableNativeFeedback that moves with the screen as I am using the ScrollView.
The idea is that left half of the screen would move the character to the left and the right half of the screen would move it to the right. I want these controls to be fixed in the display and not move.
This is how it starts
This is after moving the scrollView a bit
I've initially tried to change value of style.left as I scrolled but that doesn't seem to be a good/stable solution.
Here is the current code:
render() {
return (
<ScrollView
//onScroll={this._onScroll}
ref={(scrollView) => { this.scrollView = scrollView; }}
style={styles.container}
horizontal= {true}
snapToAlignment={"center"}
>
<View style={styles.wrapperView}>
<ImageBackground
style={styles.container}
source={require('../images/second.png')}
>
<TouchableNativeFeedback onPressIn={this._onPressButton} onPressOut={this._onPressButton}>
<View style={
{
width: width/2,
height: '100%',
position: 'absolute',
left: this.state.touchableLeft,
backgroundColor: 'red',
}
}>
</View>
</TouchableNativeFeedback>
... (code about the character)
</ImageBackground>
</View>
</ScrollView>
);
}
and the styles code
const styles = StyleSheet.create({
container: {
width: '100%',
height: '100%',
},
wrapperView:{
width: width*3 + 300,
},
});
and just to have it as a reference, this is what I originally tried:
_onScroll = (event) => {
this.setState( {
touchableLeft: this.state.touchableLeft + event.nativeEvent.contentOffset.x
} )
}
I've looked at the following questions and articles but I couldn't really get to a solution that would help me. Usually people use flex to make their headers sticky above a ScrollView and that is incredibly handy but in this situation I am unsure about how to continue. Articles/Questions:
How to Get ListView Section Header to Stick
http://docs.nativebase.io/docs/examples/StickyHeaderExample.html
Sticky Component inside scrollview
What solved my problem was to take the TouchableNativeFeedback outside of the class. The class in the question was called Background and it was rendered in the class called App.
<View style={styles.container}>
<Background />
<TouchableNativeFeedback onPressIn={this._onPressButton} onPressOut={this._onPressButton}>
<View style={
{
width: '50%',
height: '100%',
position: 'absolute',
left:0,
//left: this.state.touchableLeft,
//backgroundColor: 'red',
}
}>
</View>
</TouchableNativeFeedback>
</View>
As you can see once I moved it to here, it was positioned right where I wanted, even if I move the screeen. What would be good practice, is to take it to another component class and then just call an instance of it.
Thanks to John Ruddell for the help while coming up with the solution

Lottie animation is very small compared to it's container in react native

I have an on click animation that is working perfectly, but I can't get the actual animated element to be more "zoomed" in. It looks like this:
Here's the code:
import { Animated, TouchableHighlight } from 'react-native';
import Animation from 'lottie-react-native';
// a bunch of code here
render() {
return (
<TouchableHighlight
onPress={this.animate}
underlayColor="transparent"
>
<Animation
style={{
backgroundColor: 'red',
width: 150,
height: 150,
}}
source={favoriteHeart}
progress={this.state.progress}
/>
</TouchableHighlight>
);
}
there's nothing funny going on with the parent View, so I'm really confused as to why the heart isn't taking the full width and height of the Animation view. This also makes placing this element next to either icons difficult.
You can try add some properties of Lottie Animation:
<Animation
style={{
backgroundColor: 'red',
width: 150,
height: 150,
}}
source={favoriteHeart}
progress={this.state.progress}
resizeMode = 'cover'
/>

Categories

Resources