amarcruz / react-native-text-size Goto Github PK
View Code? Open in Web Editor NEWMeasure text accurately before laying it out and get font information from your App.
License: BSD 2-Clause "Simplified" License
Measure text accurately before laying it out and get font information from your App.
License: BSD 2-Clause "Simplified" License
Dear author, you said "If the value of the lineInfoForLine is greater or equal than lineCount, this info is for the last line (i.e. lineCount - 1).", but I found that to be incorrect.
When the value of the lineInfoForLine is greater or equal than lineCount, I can't get the last line info.
Can you confirm that,thank you very much!
I'm using implementation
as suggested in my own build file, however react-native-text-size is still using compile
internally resulting in warnings when building.
.../node_modules/react-native-text-size/android/build.gradle:
Warning:Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.
It will be removed at the end of 2018. For more information see: http://d.android.com/r/tools/update-dependency-configurations.html
Due to change in react-native cli, react-native-community/cli#1537
Autolinking is no longer working with React Native 0.69
React Native autolinking to work with react-native-text-size
ios
directoryNotice that react-native-text-size is not included in the Podfile.lock
Repro repository
https://github.com/Naturalclar/react-native-notifications-repro
Seems like the TextInput width is greater than the same text in Text. If I apply the calculated width
to the Text, it displays it without a problem, but if I set the width to TextInput, it will get clipped.
Is there a good way to calculate TextInput width?
Great module, looking forward to the iOS support.
Currently the module looks like it support measuring the fontFamily
and fontSize
only. The fontWeight
could also effect the measured width, it would be great to add support for it.
is it possible with image sizes provided? thanks!
As below example, flatHeights
and measure
provide different measurement.
import TextSize from 'react-native-text-size';
const demoText = await TextSize.flatHeights({
text: ['မြန်မာစာ'],
});
const demoSingle = await TextSize.measure({
text: 'မြန်မာစာ',
});
// output: demoText: [17]
console.log(`demoText: ${JSON.stringify(demoText)}`);
// output: demoSingle: {"width":52.5,"lineCount":1,"height":31}
console.log(`demoSingle: ${JSON.stringify(demoSingle)}`);
The result should be 31 while flatHeights
produce 17.
I want to use this module on react-native-web, but the following code
const { width: partialTextWidth } = await reactNativeTextSize.measure({ text: text.slice(0, middle), fontSize: scaledFontSize, fontFamily, fontWeight, });
gives me this error: TypeError: Cannot read property 'measure' of undefined
import reactNativeTextSize from 'react-native-text-size';
When I console reactNativeTextSize
, it shows undefined
. I don't know what is causing the issue. Does anyone know how I can make this module work on react-native-web?
Hi,
I'm getting following error when try this library to calculate height of text in iOS:
'Error in measure:', { [Error: Missing required text.] framesToPop: 1, code: 'E_MISSING_TEXT', domain: 'RCTErrorDomain', userInfo: null}
My code snippet: for JS - HeightMeasurementFromText.js
import React, { Component } from 'react'; import { Platform } from 'react-native';
import RNTextSize, { TSFontSpecs } from 'react-native-text-size';
const fontSpecs: TSFontSpecs = { fontFamily: (Platform.OS === 'ios' ? 'Proxima Nova' : 'Proxima Nova Bold'), fontWeight: (Platform.OS === 'ios' ? 'bold' : null), fontSize: 18, }
export const nameTextHeight = async (nameText) => { const dimension = { width: 0, height: 0, };
await RNTextSize.measure({ nameText, width: 170, ...fontSpecs, }).then(result => { console.log('result - ' + JSON.stringify(result)); dimension.width = result.width; dimension.height = result.height; }).catch((err) => { console.log('Error in measure:', err); });
return dimension; };
I'm calling this in my some other JS file as:
import { nameTextHeight } from './library/helper/HeightMeasurementFromText';
let nameTextHgt = nameTextHeight(item.name.toUpperCase());
My Environment variables are:
"react": "16.8.3",
"react-native": "^0.59.9",
"react-native-text-size": "^4.0.0-rc.1",
What's the problem in my code and implementation of your library?
Kindly suggest. Please.
Thanks in advance.
the implementation in iOS project is total different from Android project.
it need args named "text","fontSize" in Android project , but these args would fail when you pass to iOS project. it need args named "texts" "width" and so on
Android has a lineEndForLineNo
that measures how many characters would fit into a given number of lines, but this is not yet supported in iOS.
The issue was discussed in this pr:
#11
When applying a textTransform uppercase to the Text style, the measures (width & lastLineWidth) are not accurate.
This library looks very promising to me, it has a lot of options and seems to be even quite fast!
However, I wonder why line-height
is not supported yet as an option for the flatHeights
function? 🤔
We use the line-height style prop every now and then and it's a common typography setting as well.
I fix flatHeights method (Your method is not correct to say the height of the text (unfortunately)):
RCT_EXPORT_METHOD(flatHeights:(NSDictionary * _Nullable)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
// Don't use NSStringArray, we are handling nulls
NSArray *const _Nullable texts = [RCTConvert NSArray:options[@"text"]];
if (isNull(texts)) {
reject(E_MISSING_TEXT, @"Missing required text, must be an array.", nil);
return;
}
UIFont *const _Nullable font = [self scaledUIFontFromUserSpecs:options];
if (!font) {
reject(E_INVALID_FONT_SPEC, @"Invalid font specification.", nil);
return;
}
const CGFloat optWidth = CGFloatValueFrom(options[@"width"]);
const CGFloat maxWidth = isnan(optWidth) || isinf(optWidth) ? CGFLOAT_MAX : optWidth;
const CGSize maxSize = CGSizeMake(maxWidth, CGFLOAT_MAX);
// Create attributes for the font and the optional letter spacing.
const CGFloat letterSpacing = CGFloatValueFrom(options[@"letterSpacing"]);
NSDictionary<NSAttributedStringKey,id> *const attributes = isnan(letterSpacing)
? @{NSFontAttributeName: font}
: @{NSFontAttributeName: font, NSKernAttributeName: @(letterSpacing)};
NSMutableArray<NSNumber *> *result = [[NSMutableArray alloc] initWithCapacity:texts.count];
const CGFloat epsilon = 0.001;
UITextView *view = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, maxWidth, 0)];
view.textContainerInset = UIEdgeInsetsZero;
view.typingAttributes = attributes;
view.textContainer.lineFragmentPadding = 0.0;
view.textContainer.lineBreakMode = NSLineBreakByClipping;
for (int ix = 0; ix < texts.count; ix++) {
NSString *text = texts[ix];
// If this element is `null` or another type, return zero
if (![text isKindOfClass:[NSString class]]) {
result[ix] = @0;
continue;
}
// If empty, return the minimum height of <Text> components
if (!text.length) {
result[ix] = @14;
continue;
}
view.text = text;
CGSize size = [view sizeThatFits: maxSize];
const CGFloat height = MIN(RCTCeilPixelValue(size.height + epsilon), maxSize.height);
result[ix] = @(height);
}
resolve(result);
}
When "./gradlew assembleRelease" I get this error:
> Task :react-native-text-size:compileReleaseJavaWithJavac
Note: C:\Users\MyUser\Documents\Git\my-app\node_modules\react-native-text-size\android\src\main\java\com\github\amarcruz\rntextsize\RNTextSizeModule.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
It should be as easy as this:
Add a new file called react-native.config.js
in that file, add:
module.exports = {
dependency: {
platforms: {
android: {
packageImportPath:
"import com.github.amarcruz.rntextsize.RNTextSizePackage;",
packageInstance: "new RNTextSizePackage()",
},
ios: {
podspecPath: "ios/RNTextSize.podspec",
},
},
},
};
And I'm pretty sure that's it. I can create a PR if you give me access! Thanks!
We're attempting to use RNTS to estimate chat message text heights. We seem to have gotten estimates quite close on Android but on iOS they are over a screen height apart (see attachment). We're really not sure where we're going wrong or if there is a bug in estimating the height for ios.
Fontsize is pretty evenly set at 16. We are using recyclerlistview and not flatlist (which I know you've designed text height to work well with). Recyclerlistview is more performant than flatlist and ideally suited to couple with RNTS.
Here is the relevant code from our screen:
constructor(props) {
super(props);
this.state = {
//get height and width for images in chat
imageWidth: screen.width / 0.75,
imageHeight: screen.height / 0.75,
dataProvider: new DataProvider((message1, message2) => {
return message1._id !== message2._id
}).cloneWithRows(this.props.MainStore.messages),
layoutProvider: new LayoutProvider(
(index) => {
return 'LIST';
},
async (type, dim, index) => {
switch (type) {
case 'LIST':
dim.width = screen.width;
dim.height = screen.height / 0.75;
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
),
};
}
//FlatHeights code estimating starts here
async initFlatHeights() {
const flatHeights = await this.getFlatHeights();
//console.debug("ChatRoomScreen flatHeights has been initialized");
this.setState({
layoutProvider: new LayoutProvider(
(index) => {
return 'LIST';
},
(type, dim, index) => {
switch (type) {
case 'LIST':
dim.width = screen.width;
dim.height = flatHeights[index];
break;
default:
dim.width = 0;
dim.heigh = 0;
}
}
),
});
}
async getFlatHeights() {
const flatHeights = [];
const imgHeight = screen.width - 133;
const fontSpecs = {
fontFamily: (Platform.OS === 'ios') ? 'System' : 'Roboto',
fontSize: 16,
}
const margingBtm = 12;
const spaceBetweenMessages = 18;
//console.debug("chat messages count = " + this.props.MainStore.messages.length);
for (let i = 0; i < this.props.MainStore.messages.length; i++) {
const text = this.props.MainStore.messages[i].text;
const imgCount = (this.props.MainStore.messages[i].images != null) ? this.props.MainStore.messages[i].images.length : 0;
const width = imgHeight;
//const lineHeight = 22;
//console.debug("LayoutProvider: imgCount=" + imgCount + ", text=" + text);
const size = await rnTextSize.measure({
text, // text to measure, can include symbols
width, // max-width of the "virtual" container
lineHeight, // It should be greater if text contain Unicode symbols, such as emojis.
...fontSpecs, // RN font specification
})
const messageOwnerName = 30;
flatHeights[i] = 0;
if (this.props.MainStore.messages[i].userId != this.props.MainStore.userId) {
if (i != 0 &&
i < this.props.MainStore.messages.length - 1 &&
this.props.MainStore.messages[i].userId === this.props.MainStore.messages[i - 1].userId &&
this.props.MainStore.messages[i].userId === this.props.MainStore.messages[i + 1].userId) {
//middle
//console.debug("middle" + i + ": " + this.props.MainStore.messages[i].text);
} else {
if (i != 0 && this.props.MainStore.messages[i].userId === this.props.MainStore.messages[i - 1].userId) {
//first message
flatHeights[i] += messageOwnerName;
//console.debug("first" + i + ": " + this.props.MainStore.messages[i].text);
} else {
if (i < this.props.MainStore.messages.length - 1 && this.props.MainStore.messages[i].userId === this.props.MainStore.messages[i + 1].userId) {
// last message
//console.debug("last" + i + ": " + this.props.MainStore.messages[i].text);
} else {
// only a single message
flatHeights[i] += messageOwnerName;
//console.debug("single" + i + ": " + this.props.MainStore.messages[i].text);
}
}
}
}
if (this.props.MainStore.messages[i].images.length > 0) {
if (this.props.MainStore.messages[i].text != "") {
flatHeights[i] += (imgHeight * imgCount) + size.height + margingBtm + spaceBetweenMessages;
} else {
flatHeights[i] += (imgHeight * imgCount) + spaceBetweenMessages;
}
//console.debug("getFlatHeights: flatHeights[" + i + "]=" + flatHeights[i] + ", text=" + text + ", this.props.MainStore.messages[i].images.length: " + this.props.MainStore.messages[i].images.length);
} else {
flatHeights[i] += size.height + margingBtm + spaceBetweenMessages;
//console.debug("getFlatHeights: flatHeights[" + i + "]=" + flatHeights[i] + ", text=" + text + ", text.size.height: " + size.height);
}
}
return flatHeights;
}
async componentDidMount() {
await this.props.MainStore.joinRoom(this.props.roomId);
this.setState({
dataProvider: this.state.dataProvider.cloneWithRows(
this.props.MainStore.messages
),
});
this.initFlatHeights();
}
async _onEndReached() {
console.log('loading more messages');
if (!this.refreshing && !this.props.MainStore.endOfMessagesReached) {
this.refreshing = true;
await this.props.MainStore.getMessages(this.offset)
.catch(error => console.log(error.message));
this.refreshing = false;
this.offset += 25;
this.setState({
dataProvider: this.state.dataProvider.cloneWithRows(
this.props.MainStore.messages
),
});
await this.initFlatHeights();
}
}
rowRenderer = (type, message, index) => {
return (
<Animatable.View duration={250} useNativeDriver={true} animation="fadeIn">
{index != 0 &&
index < this.props.MainStore.messages.length - 1 &&
message.userId === this.props.MainStore.messages[index - 1].userId &&
message.userId === this.props.MainStore.messages[index + 1].userId ? (
<ChatMessage message={message} position={"middle"} onImagePress={this._onPressImage(message)} onProfilePress={this._onPressProfile(message)} />
) : (index != 0 &&
message.userId === this.props.MainStore.messages[index - 1].userId) ? (
<ChatMessage message={message} position={"first"} onImagePress={this._onPressImage(message)} onProfilePress={this._onPressProfile(message)} />
) : (index < this.props.MainStore.messages.length - 1 &&
message.userId === this.props.MainStore.messages[index + 1].userId) ? (
<ChatMessage message={message} position={"last"} onImagePress={this._onPressImage(message)} onProfilePress={this._onPressProfile(message)} />
) : (
<ChatMessage message={message} position={"only"} onImagePress={this._onPressImage(message)} onProfilePress={this._onPressProfile(message)} />
)}
</Animatable.View>
)
}
render() {
return (
<View
style={styles.container}
>
{this.props.MainStore.users.length > 0
? <RecyclerListView
contentContainerStyle={{ marginTop: 5 }}
onEndReached={this._onEndReached.bind(this)}
dataProvider={this.state.dataProvider}
layoutProvider={this.state.layoutProvider}
rowRenderer={this.rowRenderer}
renderFooter={this.renderFooter}
onEndReachedThreshold={screen.height}
/>
: null}
</View>
);
}
React Native Text Size version: 2.0.4
React Native version: 0.55.4
Recycler ListView version: 1.3.4
Platform(s) (iOS, Android, or both?): iOS 11.4-12
Device info (Simulator/Device? OS version? Debug/Release?): iPhones 5, 6 and 8, simulators for X/XR/XS
We have a number of instances where we have bold text within a normal text string. Is there any method currently available to RNTS that can detect bolded text as such and measure it? If not, maybe we could add a simple regex for bold text.
For example, currently, we use a library known as React Native Auto-link which permits the styling of hash-tagged terms as such:
<AutolinkComponent
style={styles.text}
text={this.props.postText}
hashtag="instagram"
mention="twitter"
linkStyle={{ fontWeight:'bold' }}
/>
Hi I'm trying to have this working on iOS, but at this point it is not even mounting as a NativeModule. I checked the code and it seems straightforward what it tries to achieve. You have a warning saying support for iOS is incomplete, do you have in mind already what needs be done to having it complete for iOS? I could help
Is there a way to use measure
function synchronous?
I have a function that I can not use async
keyword in its signature and also using this.setState
function is not feasible too, because this function is a utility among many other components and classes.
this below code return Promise
that is not what I want. I want only the inner result object contains width
and height
.
export function renderedTextSize(text: string, size: number = 14, fontFamily: string = 'someFontName') {
const fontSpecs: TSFontSpecs = {
fontFamily: fontFamily,
fontSize: size,
};
return TextSize.measure({
text,
...fontSpecs,
}).then((result: TSMeasureResult) => {
return {width: result.width, height: result.height};
}).catch((err) => {
console.log('Error in measure:', err);
});
}
When I test both chinese text and english text for Multi font. the english text height is correct. but the chinese text height is incorrect.
I'm using a custom font. It returns the text width for ios correctly. but in Android it always measures over the correct amount of width. anyway to incorporate custom fonts?
Hey @aMarCruz can you please support for macos and windows as well ?
null
and other types that are not strings must return zero, empty strings must return the minimum height of <Text>
for empty string. This in iOS is not fulfilled, the result is inverted.
I just upgraded to react-native 0.60.0 and the react-native-text-size
import became null
in builds.
Checking the Xcode project, react-native did recognize the pod but didn't include the source files with it.
I don't know how it worked before but the source_files
directive seems to be wrong:
s.source_files = 'RNTextSize/**/*.{h,m}'
After changing it to only *.{h,m}
and re-running pod install
, the source files were included in Xcode and the import works again.
here is my code:
import rnTextSize, { TSFontSpecs } from 'react-native-text-size'
.
.
.
function renderedTextSize(text: string, size: number = 14, fontFamily: string = 'moein') {
const dimension = {
width: 0,
height: 0,
};
const fontSpecs: TSFontSpecs = {
fontFamily: fontFamily,
fontSize: size,
};
rnTextSize.measure({
text, // text to measure, can include symbols
...fontSpecs, // RN font specification
}).then(result=> {
dimension.width = result.width;
dimension.height = result.height;
});
return dimension;
}
when I try to call the function, I get this error message, Cannot read property 'measure' of undefined
.
why should rnTextSize
be undefined when I import it from library?
Would it be possible to have an option to get lineInfo
for every line, instead of having to specify a line number? I'm trying to calculate the optimal font size for laying out headlines, and I'd like to be able to choose a size that has a low variance in line widths. I could imagine, for example, that setting a value of -1
for lineInfoForLine
would then return an array of lineInfo
objects.
So i spent hours trying to figure out why do i get 'React/RCTBridgeModule.h' file not found
when trying to build my project after linking react-native-text-size
, i tried both manual install and using link
. For some reason react-native-text-size
can't find required header files, even though Search Header paths seem to point at the right direction $(SRCROOT)/../../react-native/React
recursive. Also, Some people suggested unchecking Parallelize Build
but i already have that disabled.
react-native Version: 0.59.8
react-native-text-size Version: 3.0.0
Any idea how can resolve this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.