Underline placeholder in React Native - ios

How can I underline placeholder in iOS in React Native? I can underline whole TextInput (not placeholder) by:
borderBottomWidth: 1,
borderBottomColor: '#B8B8B8',
I would like to underline only placeholder of TextInput, not TextInput

I have a little workaround for you. As soon as you start typing, the underline gets removed.
Demo
Explanation
I applied the borderBottomWidth and borderBottomColor to the parent view, because you probably don't want to use multiline. If you want to use a mulitine TextInput you could apply those styles directly to the Textinput, as mentioned in the docs.
Note that some props are only available with multiline={true/false}.
Additionally, border styles that apply to only one side of the element
(e.g., borderBottomColor, borderLeftWidth, etc.) will not be applied
if multiline=false. To achieve the same effect, you can wrap your
TextInput in a View
As soon as the user has typed (the text length is greater than 0) something, this.setState({ active: true }) gets fired. After that the conditional rendering takes place here:
borderBottomColor: this.state.active === false ? 'red' : 'transparent' // hide color, when text is present
width: this.state.active === false ? scaledWidth : 100 // increase width to 100. Value is just a placeholder, use whatever you like
Complete Code
// helper function to scale font size and placeholder width.
scale = (fontSize) => {
const screenWidth = Dimensions.get('window').width;
const ratio = fontSize / 375; // get ratio based on your standard scale, here: width of iphone 7
const newSize = Math.round(ratio * screenWidth);
return newSize;
}
render() {
// you probably have to play around with those standard values
const scaledFontSize = this.scale(22);
const scaledWidth = this.scale(25);
return (
<View style={{ marginTop: 50, flex: 1, alignItems: 'flex-end' }}>
<View style={{ borderBottomWidth: 2, borderBottomColor: this.state.active === false ? 'red' : 'transparent', width: this.state.active === false ? scaledWidth : 100 }}>
<TextInput
style={{ height: 30, textAlign: 'right', fontSize: scaledFontSize }}
onChangeText={(text) => text.length > 0 ? this.setState({ active: true }) : this.setState({ active: false })}
placeholder={'10'}
//multiline
/>
</View>
</View>
)
}

Related

react native image from filesystem not displaying in ios

The react native image does not appear but takes up space. It's a file on the phone's filesystem, NOT the camera roll. It's the product of react-native-image-editor.
Tried using path with and without 'file://' prefix. Works fine on android. If i take the uri from the IOS simulator and paste it into chrome the picture appears. Using picture from the web doesn't work either which makes me think it's something to do with the styling.
const ImageAnalysis = (props) => {
const windowWidth = useWindowDimensions().width;
console.log(props.source);
const imageSource = props.source;
// const imagey = require(props.source.uri);
const realPath =
Platform.OS === 'ios'
? imageSource.uri.replace('file://', '')
: imageSource.uri;
console.log(realPath);
console.log(windowWidth);
return (
<View
style={{
flex: 2,
flexDirection: 'row',
borderBottomColor: colors.BrinkPink,
// backgroundColor: colors.RoyalPurple,
borderBottomWidth: 1,
}}>
<Image
source={{uri: props.source.uri}}
resizeMode="contain"
style={{
// position: 'relative',
height: windowWidth / 3,
width: windowWidth / 3,
borderRadius: windowWidth / 3,
margin: windowWidth / 12,
alignSelf: 'center',
}}
/>
Turns out it was a bug with React Native 0.63.0 and iOS 14. Upgrading to React Native 0.63.2 fixed it.

React Native multi-line vertically centered text on IOS trouble

I'm using a multi line TextInput in my react-native application and been stuck on this for a while. I can not get the text to be vertically aligned on IOS devices.
using textAlign='center' puts the text on IOS vertically centered... but it becomes an unwrapped never ending line.
adding multiline={true} negates the vertically aligned text in IOS and puts it at the top of the input.
<TextInput
style={{
width: wp('80%'),
height: hp('25%'),
borderWidth: 1,
borderRadius: 10,
fontSize: RF(3),
}}
textAlign={'center'}
multiline={true}
onChangeText={entry => this.setState({entry})}
value={this.state.entry}
/>
I would like the behavior to be like android in that it shows the placeholder text vertically and horizontally centered and when user inputs more text it starts creating multi lines if needed but always vertically and horizontally centered.
Please see image with android version on left and IOS on right.
Android on left, IOS on right
try adding textAlignVertical={"center"} to textInput's props
Have you tried wrapping the TextInput in a View?
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
<TextInput
style={{
width: wp('80%'),
height: hp('25%'),
borderWidth: 1,
borderRadius: 10,
fontSize: RF(3),
}}
textAlign={'center'}
multiline={true}
onChangeText={entry => this.setState({entry})}
value={this.state.entry}
/>
</View>
Add the attribute paddingTop to your TextInput in your XML file.

VictoryLegend: how to relatively position (e.g. bottom center)

I'm using VictoryCharts, specifically the VictoryLegend component to render the legend for my chart. According to the docs it sounds like the only options for positioning the legend are absolute x and y coordinates. I'm trying to position the legend relatively, for example "at the bottom in the middle". This is the desired appearance:
Because the series labels in my legend are internationalized, the number of characters and thus the legend's width changes based on locale, so it isn't an option to hard-code an x coordinate to center the legend. I would also prefer not to have to calculate the y coordinate based on the height of my chart. Is there any way to relatively position the VictoryLegend below the chart and horizontally centered?
According to the maintainer in a Gitter conversation, it appears this is not possible:
This is possible, but there's nothing entirely built-in with Victory charts to do it. The library has an amazing amount of flexibility but a terrible dearth of sensible defaults.
To begin, you first have to backfill basic chart responsiveness as outlined here:
https://github.com/FormidableLabs/victory/issues/396#issuecomment-773791721
Then you can use the same boundingRect width/height to position the legend as well:
https://codesandbox.io/s/loving-fog-f3ed9d?file=/index.js
Or the relevant code pieces here:
const Chart = () => {
//NOTE victory charts don't automatically resize width 🤦‍♂️ yet - https://github.com/FormidableLabs/victory/issues/396
const [boundingRect, setBoundingRect] = useState({ width: 0, height: 0 });
const containerRef = useCallback((node) => {
if (node !== null) {
setBoundingRect(node.getBoundingClientRect());
}
}, []);
//...
return (
<div ref={containerRef} style={{ width: "90vw", height: "90vh" }}>
<VictoryChart
domainPadding={30}
theme={VictoryTheme.material}
height={boundingRect.width * 0.9}
width={boundingRect.width}
>
<VictoryBar
data={chartData}
cornerRadius={2}
style={{ data: { fill: ({ datum }) => datum.fill } }}
/>
<VictoryLegend
data={legendData}
centerTitle
gutter={20}
itemsPerRow={2}
orientation="horizontal"
rowGutter={{ top: 0, bottom: -10 }}
style={{ title: { fontSize: 20 } }}
titleOrientation="bottom"
x={50}
y={boundingRect.width * 0.81}
/>
</VictoryChart>
</div>
);
};

How to make fixed width for item separator in a FlatList with flexbox?

I want to make a grid of elements with a separator with fixed width of 1 "pixel". Like in a Photo app grid for iOS.
For item width in a FlatList I use flex style, because I can not predict their width on different screens.
Code example: https://snack.expo.io/ryK_zPmEM
But on my iPhone 7 Plus (real device) i have got this result:
The separator between the second and the third cell has a style marginLeft: 1 but looks wider than the other two separators.
And this is a screenshot of the simulator for iPhone 5S:
The result is the opposite. You can also notice that the thickness of the separator between the rows is also different.
The question is: what value should be set for margin style so that separator looked the same for all screen sizes and did not change its width between the cells int the row?
Or perhaps I need to calculate the exact value for the width of the cell instead of using flex: 1?
Full code example:
const WINDOW_WIDTH = Dimensions.get('window').width
const ITEMS_PER_ROW = 4
const MARGIN_WIDTH = 1
const TOTAL_MARGIN_WIDTH = MARGIN_WIDTH * (ITEMS_PER_ROW - 1)
const CELL_SIZE = (WINDOW_WIDTH - TOTAL_MARGIN_WIDTH) / ITEMS_PER_ROW
const listData = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
export default class App extends Component {
_renderItem = ({item, index}) => {
return (
<View style={[styles.cell, (index % 4 !== 0) ? styles.cellMargin : {}]}>
<Text>{item}</Text>
</View>
)
}
render() {
return (
<View style={styles.container}>
<FlatList
contentContainerStyle={styles.list}
columnWrapperStyle={styles.listColumn}
data={listData}
numColumns={ITEMS_PER_ROW}
keyExtractor={i => i}
renderItem={item => this._renderItem(item)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'red',
},
list: {
backgroundColor: 'cyan',
flex: 1,
},
listColumn: {
marginTop: 1,
height: CELL_SIZE,
backgroundColor: 'white',
},
cell: {
backgroundColor: 'green',
flex: 1,
},
cellMargin: {
marginLeft: MARGIN_WIDTH,
}
});

React Native Responsive Font Size

I would like to ask how react native handle or do the responsive font. For example in iphone 4s i Have fontSize: 14, while in iphone 6 I have fontSize: 18.
You can use PixelRatio
For example:
var React = require('react-native');
var {StyleSheet, PixelRatio} = React;
var FONT_BACK_LABEL = 18;
if (PixelRatio.get() <= 2) {
FONT_BACK_LABEL = 14;
}
var styles = StyleSheet.create({
label: {
fontSize: FONT_BACK_LABEL
}
});
Edit:
Another example:
import { Dimensions, Platform, PixelRatio } from 'react-native';
const {
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT,
} = Dimensions.get('window');
// based on iphone 5s's scale
const scale = SCREEN_WIDTH / 320;
export function normalize(size) {
const newSize = size * scale
if (Platform.OS === 'ios') {
return Math.round(PixelRatio.roundToNearestPixel(newSize))
} else {
return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2
}
}
Usage:
fontSize: normalize(24)
You can go one step further by allowing sizes to be used on every <Text /> components by pre-defined sized.
Example:
const styles = {
mini: {
fontSize: normalize(12),
},
small: {
fontSize: normalize(15),
},
medium: {
fontSize: normalize(17),
},
large: {
fontSize: normalize(20),
},
xlarge: {
fontSize: normalize(24),
},
};
We use a simple, straight-forward, scaling utils functions we wrote:
import { Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
//Guideline sizes are based on standard ~5" screen mobile device
const guidelineBaseWidth = 350;
const guidelineBaseHeight = 680;
const scale = size => width / guidelineBaseWidth * size;
const verticalScale = size => height / guidelineBaseHeight * size;
const moderateScale = (size, factor = 0.5) => size + ( scale(size) - size ) * factor;
export {scale, verticalScale, moderateScale};
Saves you some time doing many ifs. You can read more about it on my blog post.
Edit: I thought it might be helpful to extract these functions to their own npm package, I also included ScaledSheet in the package, which is an automatically scaled version of StyleSheet.
You can find it here: react-native-size-matters.
adjustsFontSizeToFit and numberOfLines works for me. They adjust long email into 1 line.
<View>
<Text
numberOfLines={1}
adjustsFontSizeToFit
style={{textAlign:'center',fontSize:30}}
>
{this.props.email}
</Text>
</View>
Because responsive units aren't available in react-native at the moment, I would say your best bet would be to detect the screen size and then use that to infer the device type and set the fontSize conditionally.
You could write a module like:
function fontSizer (screenWidth) {
if(screenWidth > 400){
return 18;
}else if(screenWidth > 250){
return 14;
}else {
return 12;
}
}
You'll just need to look up what the default width and height are for each device. If width and height are flipped when the device changes orientation you might be able to use aspect ratio instead or just figure out the lesser of the two dimensions to figure out width.
This module or this one can help you find device dimensions or device type.
I managed to overcome this by doing the following.
Pick the font size you like for the current view you have (Make sure
it looks good for the current device you are using in the
simulator).
import { Dimensions } from 'react-native' and define the width
outside of the component like so: let width = Dimensions.get('window').width;
Now console.log(width) and write it down. If your good looking font
size is 15 and your width is 360 for example, then take 360 and
divide by 15 ( = 24). This is going to be the important value that
is going to adjust to different sizes.
Use this number in your styles object like so: textFontSize: { fontSize = width / 24 },...
Now you have a responsive fontSize.
import { Dimensions } from 'react-native';
const { width, fontScale } = Dimensions.get("window");
const styles = StyleSheet.create({
fontSize: idleFontSize / fontScale,
});
fontScale get scale as per your device.
Take a look at the library I wrote: https://github.com/tachyons-css/react-native-style-tachyons
It allows you to specify a root-fontSize (rem) upon start, which you can make dependent of your PixelRatio or other device-characteristics.
Then you get styles relative to your rem, not only fontSize, but paddings etc. as well:
<Text style={[s.f5, s.pa2, s.tc]}>
Something
</Text>
Expanation:
f5is always your base-fontsize
pa2 gives you padding relative to your base-fontsize.
I simply use the ratio of the screen size, which works fine for me.
const { width, height } = Dimensions.get('window');
// Use iPhone6 as base size which is 375 x 667
const baseWidth = 375;
const baseHeight = 667;
const scaleWidth = width / baseWidth;
const scaleHeight = height / baseHeight;
const scale = Math.min(scaleWidth, scaleHeight);
export const scaledSize =
(size) => Math.ceil((size * scale));
Test
const size = {
small: scaledSize(25),
oneThird: scaledSize(125),
fullScreen: scaledSize(375),
};
console.log(size);
// iPhone 5s
{small: 22, oneThird: 107, fullScreen: 320}
// iPhone 6s
{small: 25, oneThird: 125, fullScreen: 375}
// iPhone 6s Plus
{small: 28, oneThird: 138, fullScreen: 414}
We can use flex layout and use adjustsFontSizeToFit={true} for responsive font sizes.And the text would adjust according to the size of the container.
<Text
adjustsFontSizeToFit
style={styles.valueField}>{value}
</Text>
But in styles you need to put a fontsize as well only then will adjustsFontSizeToFit work.
valueField: {
flex: 3,
fontSize: 48,
marginBottom: 5,
color: '#00A398',
},
Why not using PixelRatio.getPixelSizeForLayoutSize(/* size in dp */);, it's just the same as pd units in Android.
I'm usually using this :
import React from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
var heightY = Dimensions.get("window").height;
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.textStyle}>fontSize {heightY * 0.014}</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
textStyle: {
fontSize: heightY * 0.014,
}
})
The idea is to get the fontSize depending on height of your screen. Example calculation:
// Height 785,.. -> fontSize = 11
// Height 1000 -> fontSize = 14
// Height 1285,.. -> fontSize = 18
You can also try using this if you want it to depend on your screen width:
var widthX = Dimensions.get("window").width;
I recently ran into this problem and ended up using react-native-extended-stylesheet
You can set you rem value and additional size conditions based on screen size. As per the docs:
// component
const styles = EStyleSheet.create({
text: {
fontSize: '1.5rem',
marginHorizontal: '2rem'
}
});
// app entry
let {height, width} = Dimensions.get('window');
EStyleSheet.build({
$rem: width > 340 ? 18 : 16
});
Need to use this way I have used this one and it's working fine.
react-native-responsive-screen
npm install react-native-responsive-screen --save
Just like I have a device 1080x1920
The vertical number we calculate from height **hp**
height:200
200/1920*100 = 10.41% - height:hp("10.41%")
The Horizontal number we calculate from width **wp**
width:200
200/1080*100 = 18.51% - Width:wp("18.51%")
It's working for all device
A slightly different approach worked for me :-
const normalize = (size: number): number => {
const scale = screenWidth / 320;
const newSize = size * scale;
let calculatedSize = Math.round(PixelRatio.roundToNearestPixel(newSize))
if (PixelRatio.get() < 3)
return calculatedSize - 0.5
return calculatedSize
};
Do refer Pixel Ratio as this allows you to better set up the function based on the device density.
You can use something like this.
var {height, width} = Dimensions.get('window');
var textFontSize = width * 0.03;
inputText: {
color : TEXT_COLOR_PRIMARY,
width: '80%',
fontSize: textFontSize
}
Hope this helps without installing any third party libraries.

Resources