hai i am trying to move the view up when keyboard as shown using react-native,I followed the #sherlock's comment in (How to auto-slide the window out from behind keyboard when TextInput has focus? i got an error like this
I don't know how to resolve this error, can any one help me how to resolve this, any help much appreciated.
There's a great discussion about this in the react-native github issues
https://github.com/facebook/react-native/issues/3195#issuecomment-147427391
I'd start there, but here are a couple more links you may find useful, one of which is mentioned already in the article you referenced...
[React Tips] Responding to the keyboard with React Native
Andr3wHur5t/react-native-keyboard-spacer
In my library "react-native-form-generator" (https://github.com/MichaelCereda/react-native-form-generator) i did the following.
I created a Keyboard Aware scroll view (partially modified from https://github.com/facebook/react-native/issues/3195#issuecomment-146518331)
the following it's just an excerpt
export class KeyboardAwareScrollView extends React.Component {
constructor (props) {
super(props)
this.state = {
keyboardSpace: 0,
}
this.updateKeyboardSpace = this.updateKeyboardSpace.bind(this)
this.resetKeyboardSpace = this.resetKeyboardSpace.bind(this)
}
updateKeyboardSpace (frames) {
let coordinatesHeight = frames.endCoordinates.height;
const keyboardSpace = (this.props.viewIsInsideTabBar) ? coordinatesHeight - 49 : coordinatesHeight
this.setState({
keyboardSpace: keyboardSpace,
})
}
resetKeyboardSpace () {
this.setState({
keyboardSpace: 0,
})
}
componentDidMount () {
// Keyboard events
DeviceEventEmitter.addListener('keyboardWillShow', this.updateKeyboardSpace)
DeviceEventEmitter.addListener('keyboardWillHide', this.resetKeyboardSpace)
}
componentWillUnmount () {
DeviceEventEmitter.removeAllListeners('keyboardWillShow')
DeviceEventEmitter.removeAllListeners('keyboardWillHide')
}
scrollToFocusedInput (event, reactNode, extraHeight = 69) {
const scrollView = this.refs.keyboardScrollView.getScrollResponder();
setTimeout(() => {
scrollView.scrollResponderScrollNativeHandleToKeyboard(
reactNode, extraHeight, true
)
}, 220)
}
render () {
return (
<ScrollView
ref='keyboardScrollView'
keyboardDismissMode='interactive'
contentInset={{bottom: this.state.keyboardSpace}}
showsVerticalScrollIndicator={true}
style={this.props.style}>
{this.props.children}
</ScrollView>
)
}
Then i use it like any other scrollview
import { KeyboardAwareScrollView } from 'react-native-form-generator'
...
handleFormFocus(event, reactNode){
this.refs.scroll.scrollToFocusedInput(event, reactNode)
}
...
<KeyboardAwareScrollView ref='scroll'>
<Form ref='registrationForm'
onFocus={this.handleFormFocus.bind(this)}
onChange={this.handleFormChange.bind(this)}
label="Personal Information">
........
</Form>
</KeyboardAwareScrollView>
on change my component (Form) will call scrollToFocusedInput in KeyboardAwareScrollView (using the ref).
i suggest to check the code of my library (see the link on top), or simply use it (everything it's already tested and working).
If you have further questions just comment
Related
LayoutAnimation not working on android
I didn't find any issues like this, and these errors are not found in google)
"react-native": "0.67.4"
I try to use LayoutAnimation, my code looks like this:
constructor(props) {
super(props)
this.state = {
height: Dimensions.get('window').height,
sliderTopPosition: Dimensions.get('window').height/2-50
}
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
kbShow = (event) => {
LayoutAnimation.configureNext(LayoutAnimation.create(
event.duration,
LayoutAnimation.Types[event.easing] //'keyboard'
// LayoutAnimation.Types.easeInEaseOut
), () => {
// console.log(finish, 'finish')
}, () => {
console.log('KEYBOARD SHOW ANIMATION DID FAIL')
})
this.setState({
height: event.endCoordinates.screenY,
sliderTopPosition: this.state.sliderTopPosition-event.endCoordinates.height+150,
})
}
And it works fine on iOS, but if I try to run this code on android I get an error:
Unsupported interpolation type : keyboard
Ok, I replace this strings like this just for test:
//LayoutAnimation.Types[event.easing] //'keyboard'
LayoutAnimation.Types.easeInEaseOut
It still works on ios but not on android
And now error is like this:
Invalid layout animation : {NativeMap: {"type": easeInEaseOut}}
And no matter what type of easing I use it always throw this error
To make it work on Android, you need to supply a property:
LayoutAnimation.configureNext(LayoutAnimation.create(
event.duration,
LayoutAnimation.Types[event.easing],
'opacity'
)
The property can be either 'opacity', 'scaleX', 'scaleY' or 'scaleXY'.
It's a very misleading error message and the type checking should show that the property is required...
I have a flat list and it contains a list of posts and videos, now I need to count the views for each item, I tried using viewabilityConfig and onViewableItemsChanged in the flat list and it works fine if I only want to count visible item as a view.
problem is that I had a function written inside onViewableItemsChanged for pausing video right after user scrolls from video.
now I have two functions with different execution times. how can I handle these two functions ?
onViewableItemsChanged = (info) => {
if (this.props.enableView) {
console.log('item viewed after minimumViewavlitytime 5s: ', info) //done after 5s of view time
}
this.pauseVideoFunction(); //done exactly after scrolling
};
viewabilityConfig = {
minimumViewTime:5000,
waitForInteraction: false,
viewAreaCoveragePercentThreshold: 95,
}
i handled it this way:
removed minimumViewTime from config file
added this to constructor since its a class component
this.debouncedViewCount = debounce(this.countView, 5000);*
called debouncedViewCount inside onViewableItemsChanged
now I have a denounced view counter which runs after 5 second if scroll doesn't move and can pause video right after scrolling
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.debouncedViewCount = debounce(this.countView, 5000); //aaaaaaaaaaa
}
countView = (info) => {
debounce(console.log('Debouced 5s', info), 5000, false)
}
onVideoExitScreen = (info) => { //todo working on view aaaaaaaaaaaaaaaaaaaa
if (this.props.enableView) {
this.debouncedViewCount(info);
}
pauseVideoFunction();
};
}
//inside Flatlist
onViewableItemsChanged={this.onViewableItemsChanged}
I need to perform different action on single and double tap on a view. On double tap I need to like the image just like Instagram double tap experience. On single tap I need to open a modal.
For double tap I have used TapGestureHandler which works perfect
<TapGestureHandler
ref={this.doubleTapRef}
maxDelayMs={200}
onHandlerStateChange={this.onProductImageDoubleTap}
numberOfTaps={2}
>
<SomeChildComponent ...
But when I add any Touchable to detect single tap in the
<TouchableWithoutFeedback onPress={this.imageTapped}>
on double tapping the this.imageTapped function is called twice along with this.onProductImageDoubleTap. Is there any way to cancel tap on touchable when two taps are done is quick succession
The best solution is not using state, since setting state is asynchronous.Works like a charm for me on android !
let lastPress = 0;
const functionalComp = () => {
const onDoublePress = () => {
const time = new Date().getTime();
const delta = time - lastPress;
const DOUBLE_PRESS_DELAY = 400;
if (delta < DOUBLE_PRESS_DELAY) {
// Success double press
console.log('double press');
}
lastPress = time;
};
return <View
onStartShouldSetResponder =
{(evt) => onDoublePress()}>
</View>
}
2022 update
This is a performant native solution without any JS thread blocking calculation!
Many more tips here
const tap = Gesture.Tap()
.numberOfTaps(2)
.onStart(() => {
console.log('Yay, double tap!');
});
return (
<GestureDetector gesture={tap}>
{children}
</GestureDetector>
);
The best solution is use react-native-gesture-handler
https://github.com/software-mansion/react-native-gesture-handler
Here is my solution -
import {State, TapGestureHandler} from 'react-native-gesture-handler';
export const DoubleTap = ({children}: any) => {
const doubleTapRef = useRef(null);
const onSingleTapEvent = (event: any) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log("single tap 1");
}
};
const onDoubleTapEvent = (event: any) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log("double tap 1");
}
};
return (
<TapGestureHandler
onHandlerStateChange={onSingleTapEvent}
waitFor={doubleTapRef}>
<TapGestureHandler
ref={doubleTapRef}
onHandlerStateChange={onDoubleTapEvent}
numberOfTaps={2}>
{children}
</TapGestureHandler>
</TapGestureHandler>
);
};
Now we will wrap the component where we need to detect the double and single tap : -
<DoubleTap>
<View>
...some view and text
</View>
</DoubleTap>
The package react-native-double-tap seems to be what you are looking for.
since you are asking on handling one tap and double tap, here's a simple code i think should covered your issue
Untested
first defined clickCount:0 in state:
state={clickCount:0, //another state}
then create a function with setTimeout to handling if user tapping once or two times:
handlingTap(){
this.state.clickCount==1 ?
//user tap twice so run this.onProductImageDoubleTap()
this.onProductImageDoubleTap :
//user tap once so run this.imageTapped with setTimeout and setState
//i set 1 sec for double tap, so if user tap twice more than 1 sec, it's count as one tap
this.setState({clickCount:1}, ()=>{
setTimeout(()=>{
this.setState({clickCount:0})
this.imageTapped()
}, 1000)
})
}
<TouchableWithoutFeedback onPress={this.handlingTap()}/>
just used TouchableWithoutFeedback instead of TapGestureHandler
With hooks:
const [lastPressed, setLastPressed] = useState(0);
const handlePress = useCallback(() => {
const time = new Date().getTime();
const delta = time - lastPressed;
setLastPressed(time);
if (lastPressed) {
if (delta < DOUBLE_PRESS_DELAY) {
console.log('double press');
} else {
console.log('single press');
}
}
}, [lastPressed]);
I have modified flix's answer into this. By this way, you can catch one and double click separately. I also changed debounce into 300ms which is fairly well for one and double click.
state={clickCount:0, //another state}
Binding context into the handlingTap method
constructor(props){
super(props)
this.handlingTap = this.handlingTap.bind(this)
}
With this function you can catch them separately
handlingTap() {
this.state.clickCount === 1
? this.doubleClick() // This catches double click
: this.setState(state => ({ clickCount: state.clickCount + 1 }), () => {
setTimeout(() => {
if (this.state.clickCount !== 2) {
this.oneClick() // this catches one click
}
this.setState({ clickCount: 0 })
}, 300)
})
}
In the button you can use this way
<TouchableWithoutFeedback onPress={this.handlingTap}></TouchableWithoutFeedback>
I'm trying to write a hacky fix to ScrollableTabView since it isn't playing nice with the function that triggers when there's a tab switch. When I replace the setState with console.log I see that it only triggers once with every tab switch so it's not looping infinitely like the error is complaining.
Parent container
state = {
headerName: 'Loading',
}
setHeader = (header) => {
this.setState({'headerName': header})
}
render () {
return (
<ScrollableTabView
renderTabBar={() => <BottomTabBar setHeader={this.setHeader} headerNames={['A','B','C']} />}
>
)
}
BottomTabBar
render() {
this.props.setHeader(this.props.headerNames[this.props.activeTab])
...
}
I want to border margin of of screen S on phone and tablet to be different. There are variants for tablet landscape and portrait mode.
How to create different margin dimension for the variants on phone, tablet portrait, tablet landscape ?
For those curious how to do on Android , we just create some resource files at the right folder :
values for default
values-sw600dp for tablet default
values-sw600dp-land for tablet landscape
The other answers have already addressed the screen detection task. However, there is still the issue of detecting if the code is running on a Tablet device. You can detect that using the react-native-device-info package, in particular its isTablet method. So, as an example, in your component:
constructor(){
super();
this.state = {orientation: 'UNKNOWN'}
this._onOrientationChanged = this._onOrientationChanged.bind(this);
}
_onOrientationChanged(orientation){
this._setState({orientation})
}
componentDidMount(){
Orientation.addOrientationListener(this._onOrientationChanged);
}
componentWillUnmount(){
Orientation.removeOrientationListener(this._orientationDidChange);
}
render(){
let layoutStyles;
if(DeviceInfo.isTablet()){
layoutStyles = this.state.orientation == 'LANDSCAPE' ? landscapeTabletStyle : portraitTabletLandscape; // Basic example, this might get more complex if you account for UNKNOWN or PORTRAITUPSIDEDOWN
}else{
layoutStyles = this.state.orientation == 'LANDSCAPE' ? landscapeStyle : portraitLandscape;
}
render(){
<View style={[styles.container, layoutStyles]} // And so on...
}
}
Note that the state holds the UNKNOWN value on the beginning. Have a look at the getInitialOrientation() of the package function. I am intentionally leaving that bit out because it simply reads a property that is set when the JS code loads, and I am not sure if that satisfies your usecase (i.e. this is not your first screen). What I usually like to do is store the rotation value in a redux store (where I initialize the orientation value to that of getInitialOrientation() and then subscribe only once to the orientation listener).
I think this library will be helpful for you: https://github.com/yamill/react-native-orientation
You can do something like that with it:
Orientation.getOrientation((err,orientation)=> {
console.log("Current Device Orientation: ", orientation);
if(orientation === 'LANDSCAPE') {
//do stuff
} else {
//do other stuff
}
});
// Extract from the root element in our app's index.js
class App extends Component {
_onLayout = event => this.props.appLayout(event.nativeEvent.layout);
render() {
return (
<View onLayout={this._onLayout}>
{/* Subviews... */}
</View>
);
}
}
export const SET_ORIENTATION = 'deviceStatus/SET_ORIENTATION';
export function appLayout(event: {width:number, height:number}):StoreAction {
const { width, height } = event;
const orientation = (width > height) ? 'LANDSCAPE' : 'PORTRAIT';
return { type: SET_ORIENTATION, payload: orientation };
}
Code Copied from Here