How to delay AppLoading in React Native? Some elements load faster than others? - ios

Im developing a React Native app in Expo and I have a custom splash screen animation that I have up initially after the AppLoading splash screen disappears. This works in simulator - the animate out transition is an Animated.timing moving from 1 to 0.
Problem is, on an actual iPhone, theres a split second where the AppLoading disppears and the secondary animated splash screen (an Animated.Image) isn't behind it. Other elements of the app have loaded in that split second though.
This happens even if I take out the "animate out" animation. So my logic is to delay the AppLoading disappearing by 1 second long enough for the secondary splash to be steadily in place.
How can I do this? What is happening here?
EDIT: Only mention of AppLoading is here:
if (!fontsLoaded) {
return <AppLoading />;
} else {
The animated splash screen elements
<Animated.Image pointerEvents={"none"} style={[styles.splash, { opacity: splashOpacity }]} source={require('./assets/splashnew.png')} />
<Animated.Image pointerEvents={"none"} style={[styles.splashTxt, { opacity: splashOpacity, transform: [{scaleY: splashScale }, {scaleX: splashScale }]} ]} source={require('./assets/splash.png')} />
Are controlled by this function:
useEffect(() => {
Animated.sequence([
Animated.delay(1000),
Animated.spring(splashScale, {
toValue: 1,
bounciness: 4,
useNativeDriver: false,
speed: 2
})
]).start()
const interval = setInterval(() => {
Animated.sequence([
Animated.parallel([
Animated.spring(splashScale, {
toValue: 0,
bounciness: 4,
useNativeDriver: false,
speed: 2
}),
Animated.spring(splashOpacity, {
toValue: 0,
bounciness: 2,
useNativeDriver: false,
speed: 3
}),

all you have to do is setup another state variable and use that instead of fontsLoaded.
const [waitOneSecond, setWaitOneSecond] = useState(false);
useEffect(()=>{
if (fontsLoaded) setTimeOut(()=>{setWaitOneSecond(true);},1000);
},[fontsLoaded]);
if (!waitOneSecond) {
return <AppLoading />;
} else {

Related

How to implement a "goBack" Function to return to the initial navigator?

This is my first post, so sorry for any errors.
I'm trying to make a series of nested navigators, starting from a Switch navigator, then going to a Drawer Navigator, that for each of it's sections, it calls a different Stack Navigator, like this:
SwitchNavigator
DrawerNavigator
MapStackNavigator
Map Screen
ProfileStackNavigator
Profile Screen
The initial route for the Drawer Nav is the Stack Nav which calls a Map Screen. The problem is, if I go to the "Profile" Section of the drawer nav, which calls for the Stack Nav that opens the Profile Screen, I'm not beeing able to return to the Map Screen through an onPress() method inside the Navigation headerLeft Icon.
The react-native app is being developed for iOS only, and now as I don't have an iOS device to test on, neither a Mac to use a simulator, I'm currently testing it on my android smartphone.
As I've tested, it wouldn't be a problem if the app was meant to run on android, as the android has the built-in "back" button and it automatically goes back to the previous screen (which is set to the Map Screen inside the DrawerNav). But, as it is for iOS only, we actually need to display a "back" button on the top of the screen (so, inside the StackNav Header) for going back to the Map Screen.
My first guess was to use the navigation.navigate inside the onPress() to call another screen from the current profile StackNav, and that screen calls the first SwitchNav that I've Created to load things up, but it won't work, as it acts like a "navigation loop", and the const SwitchNavigator can't be called from the Profile StackNav.
I've tried to call the StackNav that holds the MapScreen from the onPress(), so it was: click on the back button > it calls the onPress() which calls another screen > that another screen calls the StackNav holding the MapScreen, but it nests one StackNav inside the other.
My Routes.js looks like this:
import Testemapa from './src/components/Map/index';
import MyProfile from './src/components/DrawerNavigator/MyProfile';
import Icon from 'react-native-vector-icons/Ionicons';
import IconE from 'react-native-vector-icons/Entypo';
// The StackNav that holds the Map Screen
const IvesStackNavigator = createStackNavigator ({
screenMap: Testemapa,
},
{
defaultNavigationOptions: ({navigation})=>{
return{
headerRight: (<Icon
style={{paddingRight: 10}}
onPress={() => navigation.openDrawer()}
name="md-menu" size={30}
/>)
};
}
});
// What I tried to do to call the MapScreen, but it ends up nesting it inside
const wayBackSwitchNavigator = createSwitchNavigator ({
transitionToHome: IvesStackNavigator,
});
// The StackNav that holds the Profile Screen
const myProfileStackNavigator = createStackNavigator ({
drawer_myProfile: MyProfile,
transitionPathHome: wayBackSwitchNavigator,
},
{
headerBackTitleVisible: 'true',
defaultNavigationOptions: ({navigation})=>{
return{
//This guy is the Back button, that's actually an Icon
headerLeft: (<IconE
style={{paddingLeft: 10}}
onPress={() => navigation.navigate('transitionPathHome')}
name="chevron-small-left" size={30}
/>),
};
},
});
// The Drawer which calls all of the stacks
const MainDrawerNavigator = createDrawerNavigator ({
BOOK_A_RIDE: {
screen: IvesStackNavigator,
navigationOptions: () =>
({
title: 'BOOK A RIDE'
})
},
MY_EARNINGS: {
screen: ridesStackNavigator,
navigationOptions: () =>
({
title: 'MY EARNINGS'
})
},
MY_PROFILE: {
screen: myProfileStackNavigator,
navigationOptions: () =>
({
title: 'MY PROFILE'
})
},
},
{
initialRouteName: 'BOOK_A_RIDE',
drawerOpenRoute:'DrawerOpen',
drawerPosition: 'right',
drawerBackgroundColor: 'black',
hideStatusBar: 'true',
overlayColor: 'red',
edgeWidth: 100,
contentOptions: {
inactiveBackgroundColor: 'black',
inactiveTintColor: '#fff',
activeBackgroundColor: '#4d4d4d',
activeTintColor: '#fff',
},
contentComponent: props =>
<Container style={{alignItems: 'stretch', marginLeft: 0, marginRight : 0, backgroundColor: 'black'}}>
<Header style={{height:150, marginLeft: 0, marginRight : 0, backgroundColor: 'black'}}>
<Body style={{marginLeft: 0, marginRight : 0, backgroundColor: 'black'}}>
<ImageBackground source={require('./src/assets/splash_ImageLimo.jpg')} style={{left: -10, width: 264, height:150, opacity:0.7, marginLeft: 0, marginRight : 0, paddingRight: 0}}>
</ImageBackground>
</Body>
</Header>
<Content>
<DrawerItems {...props} />
</Content>
</Container>
},
);
// The SwitchNav that is the first Navigator to run on the app
const firstSwitchNavigator = createSwitchNavigator(
{
TestDrawer: MainDrawerNavigator,
},
{
initialRouteName: 'TestDrawer',
}
);
export default createAppContainer(MySwitchNavigator);
So, when The Back button inside the MyProfileStackNavigator is pressed, It was supposed to go back to the MapScreen which is holded by the IvesStackNavigator, which is inside the mainDrawerNavigator.
I'm not allowed to let the user open the DrawerNav from the Profile Screen, so the back button is really important, as it's the only way to go back to the map.
If I try to directly call the SwitchNav or the DrawerNav by using any of them as the Screen path in MyProfileStackNavigator, I get the error that the screen that was called isn't a React component ("The component for route 'transitionToHome' must be a React component...").
If I change it to call IvesStackNavigator directly, the navigator will end up nested inside the MyProfileStackNavigator.
And, if I call the actual map page, it will display the map page inside the MyProfileStackNavigator, having all of the MyProfileStackNavigator properties (like the Back button and the absency of the button to open the drawer menu).
Any help with this would be appreciated, I'm fairly new to React-Native and it's dependencies, and I had to jump in right to coding before learning, as it is a task assigned to me by my boss, so I try to learn while producing the app.

Animate box expansion in the center of the screen (React Native Animated)

Its my first time animating with react native's animated library. Is it possible to animate in the center of the screen? If so, how can I do that?
This is my code below:
I basically had a 60x60 box on the top left corner of my screen to go down 1/2 the screen and over 1/2 the width. I want it to expand horizontally about 150 px across. The problem is that the box is not expanding in the center of the screen
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
View,
Animated,
Dimensions,
Easing
} from 'react-native';
const {width,height} = Dimensions.get('window')
export default class App extends Component {
constructor(){
super()
this.state = {
width: new Animated.Value(60),//Animated.Value/ValueXY used to hook up animation attributes, also for single values
height: new Animated.Value(60),
animateXY : new Animated.ValueXY({x:0,y:0})//for vectors
}
}
componentWillMount(){
//called when an instance of a component is being created and inserted into the DOM
Animated.sequence([
//function where we add the ending results
Animated.timing(
this.state.animateXY,
{
toValue: {x:0, y:height/2},
duration: 2000
}),
Animated.timing(
this.state.animateXY,
{
toValue: {x:width/2, y:height/2},
duration: 2000,
}),
Animated.timing(
this.state.width,
{
toValue: 150,
duration: 3000
}),
]).start()
}
render() {
return (
<View>
<Animated.View
style={
{
width: this.state.width,
height:this.state.height,
backgroundColor:"teal",
position:'absolute',
top: this.state.animateXY.y,
left: this.state.animateXY.x,
}
}
/>
</View>
);
}
}
Any advice will do! thank you!
Jemma
You are not updating the height. Maybe in your case you can use the width only since it’s a square

How to make a TouchableOpacity or TouchableHighlight clickable on devices that support 3d touch (force touch)

I've got this code:
<View {...this.panGesture.panHandlers}>
<Animated.View>
<View style={{ position: 'absolute' }}>
<TouchableHighlight onPress={ () => {console.log('hello')}>
</TouchableHighlight>
</View>
</Animated.View>
</View>
and I can't for the love of mine get the onPress to work for iPhones 6s and above, when 3d touch is enabled.
I've tried most solutions suggested here, but with no luck.
I'd appreciate any help at all!
I have been able to workaround this specific issue by making sure the parent PanResponder doesn't grab the responder on move, until the touch has actually moved from the origin:
PanResponder.create({
//... other responder callbacks
onMoveShouldSetPanResponder(e, gestureState) {
if (gestureState.dx === 0 || gestureState.dy === 0) {
return false;
}
return true;
}
});
This was back in around React Native 0.10 days, haven't tried it since. Hope it helps!
Ok after fiddling a lot with this, I realised the solution was partly what jevakallio said BUT I can no longer use Touchables when 3D touch is enabled so I have to reproduce their behaviour manually.
My code now looks like this:
The child code:
let touchable;
if (View.forceTouchAvailable === false) { // if 3d touch is disabled
// just use a Touchable and all should be fine.
touchable = (<TouchableHighlight
onPress={ () => {console.log('hello world');}
style={this.props.style}
underlayColor={this.props.highlightBgColor}
>
{this.props.children}
</TouchableHighlight>);
} else { // Otherwise if 3D touch is enabled
// we'll have to use a child view WITH a PanResponder
touchable = (<View
style={[this.props.style, {
backgroundColor: this.state.panViewIsTapped ?
this.props.highlightBgColor
:
bgColor,
}]}
{...this.panResponderChild.panHandlers}
>
{this.props.children}
</View>);
}
return (<View {...this.panResponderParent.panHandlers}>
<Animated.View>
<View style={{ position: 'absolute' }}>
{touchable}
</View>
</Animated.View>
</View>);
The child pan responder code (this.panResponderChild):
this.panResponderChild = PanResponder.create({
// true because we want tapping on this to set it as the responder
onStartShouldSetPanResponder: () => true,
// true because we want this to capture the responder lock from it's parent on start
onStartShouldSetPanResponderCapture: () => true,
// when the pan responder lock is terminated, set the pan view as NOT tapped
onPanResponderTerminate: () => {
this.setState({ panViewIsTapped: false });
},
// true so that the parent can grab our responder lock if he wan'ts to.
onPanResponderTerminationRequest: () => true,
// false because we DON'T want this btn capturing the resp lock from it's parent on move
onMoveShouldSetPanResponderCapture: () => false,
// false because we DON'T want moving the finger on this to set it as the responder
onMoveShouldSetPanResponder: () => false,
onPanResponderGrant: () => {
this.setState({ panViewIsTapped: true });
},
onPanResponderRelease: () => {
this.setState({ panViewIsTapped: false });
console.log('hello world');
},
})
The parent pan responder code (this.panResponderParent):
this.panResponderParent = PanResponder.create({
// true because we want tapping on the cal, to set it as a responder
onStartShouldSetPanResponder: () => true,
// false because we DON'T want to grab the responder lock from our children on start
onStartShouldSetPanResponderCapture: () => false,
/*
onMoveShouldSetPanResponderCapture:
false because we don't want to accidentally grab the responder lock from
our children on movement.
That's because sometimes even a small tap contains movement,
and thus a big percentage of taps will not work.
Keeping that flag false does not nessecarily mean that our children will
always capture the responder lock on movement, (they can if they want),
we're just not strict enough to grab it from them.
*/
onMoveShouldSetPanResponderCapture: () => false,
/*
onMoveShouldSetPanResponder:
We DO want moving the finter on the cal, to set it as a responder,
BUT, we don't always want moving the finger on an appointment setting this parent
as the responder.
Logic:
So, when the dx AND dy of the pan are 0, we return false, because we don't
want to grab the responder from our appointment children.
For anything other than that we just allow this parent to become the responder.
(dx, dy: accumulated distance of the gesture since the touch started)
*/
onMoveShouldSetPanResponder: (e, gestureState) =>
!(gestureState.dx === 0 || gestureState.dy === 0),
});

Control pan and zoom animation duration in mapbox.js

I'm making an animated map showing a series of points using Mapbox.js. Ideally, I want to smoothly switch focus between points by combining zoom and pan like this example created in d3.js. I wonder if there is anyway to control the pan & zoom animation speed (mainly to slow it down). In the code below, I've tried both setView() and panTo() functions and the transition is too fast. Any suggestion will be highly appreciated, thanks!
L.mapbox.accessToken = "#Token Here";
var map = L.mapbox.map('map', 'mapbox.streets')
.setView([34.01, -118.48], 5, {
pan: { animate: true },
zoom: { animate: true }
});
map.setView([33.98, -118.42], 5);
Take a look at this fiddle.
You can use a function like:
function jumpTo(index){
map.setView(positions[index], 4, {
pan: {
animate: true,
duration: 2
},
zoom: {
animate: true
}
});
}
And control it on moveend event.

Animate jQuery UI dialog auto resize

I have a dialog with a dynamic form inside that can increase the height of the dialog. autoResize is set to true, width is 500. Is there any way to animate the dialog resize when more content is added?
Animating dialog size, while staying in the center of the screen
jQuery("#dialog").dialog("widget").animate({
width: '400px',
height: '110px'
}, {
duration: 500,
step: function() {
jQuery("#dialog").dialog('option', 'position', 'center');
}
});
Originally I was using .show('fade') and the size of the dialog would jump whenever .show was called. When using the effect .show('fast') or .show('slow'), the dialog is resized in a sliding fashion which works for me.
When i was using #Steven's answer i have issues with content size, like #jedierikb said in comment. So i created this code and it works.
$(dialogSel).dialog("widget").animate({
width: 100,
height: 200
}, {
duration: 200,
step: function (now, tween) {
if (tween.prop == "width") {
$(dialogSel).dialog("option", "width", now);
} else {
$(dialogSel).dialog("option", "height", now);
}
}
});

Resources