React Native bottom modal with horizontal scroll - react-native

I'm trying to get a modal working to look like the picture below. I've tried various modal and actionsheet solutions but can't quite get it right. Does anyone know if a solution exists that can provide a similar result? Thanks]1
I have been using an action sheet from a library (pic below) but it cannot be customized to scroll horizontally and use custom buttons. I also have not yet attempted in creating my own, I first wanted to know if anyone knows of a component which will yield the same result.
Regular action sheet on iOS

Here is a simple working example as per your requirement. I am using react-native-modal for Modal component.
import React, { Component } from 'react'
import {
StyleSheet,
Text,
View,
TouchableOpacity,
ScrollView
} from 'react-native'
import Modal from 'react-native-modal'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
visible: false
}
}
showModal = () => this.setState({visible: true})
hideModal = () => this.setState({visible: false})
render() {
return (
<View style={{flex: 1}}>
<TouchableOpacity
onPress={this.showModal}
style={{alignSelf: 'center', marginTop: 50, backgroundColor: 'grey'}}
>
<Text>Touch Me</Text>
</TouchableOpacity>
<Modal
style={styles.modal}
isVisible={this.state.visible}
onBackdropPress={this.hideModal}
>
<ScrollView
horizontal={true}
>
{/* place your buttons here */}
<Text> Very Very Long String </Text>
</ScrollView>
</Modal>
</View>
)
}
}
const styles = StyleSheet.create({
modal: {
margin: 0,
backgroundColor: 'white',
height: 100,
flex:0 ,
bottom: 0,
position: 'absolute',
width: '100%'
}
})

Related

react-native-webview why is the goBack() method not working?

I have a simple React Native project in Expo, it launches a website using react-native-webview.
Here is the source code:
import React from "react";
import { StyleSheet, View, SafeAreaView } from "react-native";
import { AntDesign } from "#expo/vector-icons";
import { WebView } from "react-native-webview";
export default function App() {
const goback = () => {
WebView.goBack();
};
return (
<SafeAreaView>
<WebView source={{ uri: "https://google.co.uk" }} />
<View style={styles.navbar}>
<View style={styles.forward}>
<AntDesign name="right" size={25} color="grey" />
</View>
<View style={styles.back}>
<AntDesign name="left" size={25} color="grey" onPress={goback} />
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
navbar: {
height: 40,
width: "100%",
flexDirection: "row-reverse",
paddingTop: 6,
backgroundColor: "#fefefe",
borderTopColor: "grey",
borderTopWidth: 1,
},
back: {
width: 50,
height: 50,
marginRight: 10,
},
forward: {
width: 50,
height: 50,
},
});
The WebView component loads the website fine (google.co.uk) but I can not get the navigation to work. I simply want to create a back button, that allows the user to navigate back to other pages they have viewed in the WebView, and forward if they have gone back and want to go forward.
For now I am trying to get the back button working. I load the app, then navigate to a different page on the WebView. When the back button is pressed, the following error is generated:
TypeError: _reactNativeWebview.WebView.goBack is not a function (In
'_reactNativeWebview.WebView.goBack()','_reactNativeWebview.WebView.goBack'
is undefined)
According to the doc's the goBack() method exists:
goBack()
I found this but it is implementing a class based component so I couldn't easily map the suggestions into my functional component, and further, I think that solution is overkill as they are intercepting the navigation, I believe what I am trying to achieve should be simpler, but I can't get the basic navigation to work on the WebView (i.e. go back and forward to previously viewed pages).
Everything mentioned by you is correct Gary. The only thing that you need to change is the way how goBack function is called. goBack is not a component's direct function rather you need to pass on a reference to the WebView component to get this function. In your case you can change your component as below to get this working:-
import React, { useRef } from "react";
import { StyleSheet, View, SafeAreaView } from "react-native";
import { AntDesign } from "#expo/vector-icons";
import { WebView } from "react-native-webview";
export default function App() {
const webViewRef = useRef(null)
const goback = () => {
webViewRef.current.goBack();
};
return (
<SafeAreaView>
<WebView ref={webViewRef} source={{ uri: "https://google.co.uk" }} />
<View style={styles.navbar}>
<View style={styles.forward}>
<AntDesign name="right" size={25} color="grey" />
</View>
<View style={styles.back}>
<AntDesign name="left" size={25} color="grey" onPress={goback} />
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
navbar: {
height: 40,
width: "100%",
flexDirection: "row-reverse",
paddingTop: 6,
backgroundColor: "#fefefe",
borderTopColor: "grey",
borderTopWidth: 1,
},
back: {
width: 50,
height: 50,
marginRight: 10,
},
forward: {
width: 50,
height: 50,
},
});
This refernce will help you in calling any reference functions mentioned in the documentation of webview module. Enjoy!
also you can see different usage of webview in this package. https://github.com/ilkerkesici/react-native-beauty-webview

How to add a static background image for screens in Material Top Tab Navigator?

I have created two tabs with createMaterialTopTabNavigator from react-navigation. I like to have a single background image for two tabs.
The current behaviour is that when I swipe from tab1 to tab2, the image is also transitioned, but I like to have the background image static when transitioning from tab1 to tab2, and only the contents of the tab to transition when swiped. I have tried wrapping the TabNavigator inside the ImageBackground component, but that is of no use.
I think you can use one of the following solutions:
Style the tabs to have a transparent background and set the background image on a <View> above the navigator. You can find details about styling cards in the React Navigation docs here.
A second option, and the more elegant one I think, is to use a dedicated library for managing transitions in React Navigation. There are a few out there but I personally have used Fluid Transitions and I loved it. If you decide to use this library you can set your background image inside your StackNavigator View. You will need to add a shared prop and you'll be done :)
here is the demo: https://snack.expo.io/#nomi9995/e05080
the better way to use react-native-tab-view and wrap TabView within ImageBackground
yarn add react-native-tab-view
import React, { Component } from "react";
import {
Text,
StyleSheet,
View,
SafeAreaView,
ImageBackground,
Dimensions,
} from "react-native";
import { TabView, SceneMap } from "react-native-tab-view";
const width = Dimensions.get("window").width;
function FirstRoute() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>FirstRoute!</Text>
</View>
);
}
function SecondRoute() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>SecondRoute!</Text>
</View>
);
}
export default class App extends Component {
state = {
index: 0,
routes: [
{ key: "first", title: "First" },
{ key: "second", title: "Second" },
],
};
render() {
const { index, routes } = this.state;
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
return (
<SafeAreaView style={{ flex: 1 }}>
<ImageBackground
style={{ flex: 1, width: width }}
source={{
uri:
"https://firebasestorage.googleapis.com/v0/b/ielts-preps.appspot.com/o/1592920135765?alt=media&token=ec911583-06f9-4315-b66c-cf47de120e85",
}}
>
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={(i) => this.setState({ index: i })}
tabBarPosition="bottom"
/>
</ImageBackground>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({});

react native make part of TextInput to be bold or italics at run time

I have a simple TextInput App like below:
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = { textValue: '' };
this.handleTextInputChange = this.handleTextInputChange.bind(this);
}
handleTextInputChange(input) {
this.setState({textValue: input})
}
render() {
return (
<KeyboardAvoidingView
behavior="padding"
style={{flex:1}}
enabled>
<TextInput
style={styles.textInputStyle}
multiline={true}
onChangeText={this.handleTextInputChange}
value={this.state.textValue}
/>
</KeyboardAvoidingView>
);
}
}
What I'd like to do is when I write ##hello in TextInput, what's instantaneously rendered in TextInput screen is hello in bold just like Markdown editing in Dropbox Paper. Similarly, when I write _hello_, what I see in the screen is hello italicized.
Screen
Can I do that? (Have part of TextInput to have different styles)
So far, it seems like TextInput can only take one style?
If we cannot have different styles TextInput, what might be an alternative to make part of (some kind of TextInput) bold, italicized, bigger, smaller...
I'm pretty sure you can nest Text within TextInput like this:
<TextInput>
<Text style={{fontWeight:'bold'}}>I'm bold</Text>
</TextInput>
Just parse the text and append Text with different styles as needed.
You can use this lib react-native-easy-markdown to render markdown text and hide the text input like this and render the markdown component instead. :
import React, { Component } from 'react';
import { StyleSheet, View, TextInput, TouchableOpacity } from 'react-native';
import Markdown from 'react-native-easy-markdown';
export default class App extends Component {
state = { text: 'type here ...' };
onClick = e => {
this.textInput.focus();
};
render() {
return (
<View style={styles.container}>
<TextInput
ref={ref => (this.textInput = ref)}
style={{ position: 'absolute', left: -1000, top: -1000 }}
onChangeText={text => this.setState({ text })}
/>
<TouchableOpacity onPress={this.onClick}>
<Markdown>{this.state.text}</Markdown>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
here is a demo of the code :

why does react native display a scene above a navigator navbar component?

Consider this screenshot,
The white area on top represents a generic <View> component while the green/blue combination represents an app specific <NavBar> component mounted to the <Navigation> obj by setting the prop navigationBar=
main code from index.ios.js
var {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
} = React;
var styles = StyleSheet.create({
mainContent:{
flex:1
},
});
class BioStream extends React.Component{
render() {
return (
<Navigator navigationBar={<NavBar />} renderScene={ (route, nav) => <View style={styles.mainContent}><Text>'some text'</Text></View> } />
);
}
};
AppRegistry.registerComponent('BioStream', () => BioStream);
and main code from the NavBar component definition file,
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
} = React;
var styles = StyleSheet.create({
mainContainer:{
flex:1
},
toolbar:{
backgroundColor:'#81c04d',
paddingTop:20,
paddingBottom:10,
flexDirection:'row',
borderColor: 'black',
borderWidth: 1
},
toolbarButton:{
width: 50,
color:'#fff',
textAlign:'center'
},
toolbarTitle:{
color:'#fff',
textAlign:'center',
fontWeight:'bold',
flex:1
},
content:{
backgroundColor:'blue',
flex:1 //Step 2
}
});
class NavBar extends React.Component{
render() {
return (
<View style={styles.mainContainer} >
<View style={styles.toolbar}>
<Text style={styles.toolbarButton}>Add</Text>
<Text style={styles.toolbarTitle}>This is the title</Text>
<Text style={styles.toolbarButton}>Like</Text>
</View>
<View style={styles.content}>
</View>
</View>
)
}
};
module.exports = NavBar;
What am I failing to understand about how this layout (and/or the general nature of react native layouts) should be constructed? It is my intent to place the navbar consistently in all scenes at the top of the frame and have all scene content/code get rendered below.
It's seems because react native component render navigation bar after scene (source code here).You can set styles to your custom navigation bar to get it act well.
There's no issue related with this,you can open one on react native project to get the reason why it should be like this.
It is now handled by react native i guess (focus on style={styles.navigator}):
<Navigator
style={styles.navigator}
navigationBar={
<NavBar />
}
configureScene={this._configureScene}
initialRoute={this.state}
ref="featuresNavigator"
renderScene= {this._renderScene}
/>
And the style :
var styles = StyleSheet.create({
navigator: {
flexDirection: 'column-reverse'
},
});
More :
if the navbar don't appear, you have to set an height in its style <NavBar style={{height: 40}}/>

How can I change the text in TabBarIOS in React Native?

In the react native documentation I cannot find a way to change the bottom words?
<TabBarItemIOS
name="greenTab"
icon={_ix_DEPRECATED('more')}
accessibilityLabel="Green Tab"
selected={this.state.selectedTab === 'greenTab'}
onPress={() => {
this.setState({
selectedTab: 'greenTab',
presses: this.state.presses + 1
});
}}>
{this._renderContent('#21551C', 'Green Tab')}
</TabBarItemIOS>
What is the accessibilityLabel ?
The TabBarItem allows you to use one of the iOS preset icons from UITabBarSystemItem, and in your sample code it's using the "More" icon. Crucially though, the documentation for UITabBarSystemItem states:
The title and image of system tab bar items cannot be changed.
If you set the icon to either a data-uri or a local image, rather than an icon from UITabBarSystemItem, you'll be able to override the text on the item to whatever you want using the title prop.
You can try something like that for your TabBarIOS.Item with a custom icon
import React, { Component } from 'react'
import { AppRegistry, StyleSheet, Text, View, TouchableOpacity, TabBarIOS } from 'react-native'
const base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg=='
class ReactNativePlayground extends Component {
constructor(props) {
super(props)
this.state = {
selectedTab: "more",
tabBarItemTitle: "More"
}
}
render() {
return (
<TabBarIOS>
<TabBarIOS.Item selected={this.state.selectedTab === "more"}
title={this.state.tabBarItemTitle}
icon={{uri: base64Icon, scale: 3}}>
<View style={styles.container}>
<TouchableOpacity onPress={ (event) => { this._changeTabItemTitle() } }>
<Text style={styles.button}>Tap to Change Item Title</Text>
</TouchableOpacity>
</View>
</TabBarIOS.Item>
</TabBarIOS>
);
}
_changeTabItemTitle() {
this.setState({ tabBarItemTitle: "New More" })
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
button: {
fontSize: 20,
color: "white",
textAlign: 'center',
backgroundColor: "#1155DD",
borderRadius: 5,
height: 30,
margin: 30,
},
});
AppRegistry.registerComponent('ReactNativePlayground', () => ReactNativePlayground);