I needed to measure text to prevent a TextInput from determining the location of the text break itself, then resizing. I wanted the TextInput to fit it's content into a maximum width, so I used a patch to this package to calculate what needed to be the minimum width of the TextInput. I thought I would share since I never found any solutions to this problem online and it being a likely valuable feature to this package.
To see how what this solution solves, center a TextInput with a maxWidth. (I used a percentage amount.) The TextInput will change it's width depending on the first optimal text break found for that maxWidth of the TextInput. I never found a way to disable this behavior, so my solution to this was a work around.
My patch:
Function("findTextBreak") { (text: String, style: StyleOptions) -> Int in
if (text.count == 0) {
return 0
}
let fontSize = style.fontSize != nil ? RCTConvert.cgFloat(style.fontSize) : CGFloat(0)
let letterSpacing = style.letterSpacing != nil ? RCTConvert.cgFloat(style.letterSpacing) : CGFloat(0)
let usedFontWeight = style.fontWeight ?? "Normal"
let maxWidth = style.maxWidth ?? nil
if let font = style.fontFamily != nil ?
RCTConvert.uiFont(["fontWeight": usedFontWeight, "fontFamily": style.fontFamily!, "fontSize": fontSize, NSAttributedString.Key.kern: letterSpacing ]) : RCTConvert.uiFont(["fontWeight": usedFontWeight, "fontSize": fontSize])
{
var minima: Int = 0, maxima : Int = text.count - 1, median: Int? = nil
var currWidthCalc: Double? = nil
var maxWidthSurpassed: Bool? = nil
repeat {
if (maxima - minima > 1) { // if an index lies between the minima and maxima
median = ((maxima + minima) / 2)
} else {
median = maxima
}
let medianStrIndex = text.index(text.startIndex, offsetBy:(median ?? 0) + 1)
currWidthCalc = String(text[..<medianStrIndex]).size(withAttributes: [.font: font]).width
maxWidthSurpassed = currWidthCalc ?? Double(0) > maxWidth ?? Double(0)
if (maxima - minima == 1) {
return 1 + ((maxWidthSurpassed ?? false) ? minima : median ?? 0)
}
if (maxWidthSurpassed ?? false) { // if median point of string results in passing the maxWidth
maxima = median ?? 0 // set maximum searchable index
} else {
minima = median ?? 0 // set minimum searchable index
}
} while (true)
return 1 + ((maxWidthSurpassed ?? false) ? minima : median ?? 0)
} else {
return text.count
}
}
export function findTextBreak(
text: string,
style: { fontSize?: number; fontFamily?: string; fontWeight?: string, letterSpacing?: number, allowFontScaling?: boolean, maxWidth?: number } = {}
): number {
return ReactNativeMeasureText.findTextBreak(text, style);
}
Usage:
import React, { useState } from "react";
import { StyleSheet, TextInput, Text, View, Dimensions } from "react-native"
import * as MeasureText from "@domir/react-native-measure-text";
const textMaxWidth = Dimensions.get("window").width * .8
export default function TextMeasuring() {
const [value, setValue] = useState("")
const displayCharCnt = MeasureText.findTextBreak(value, {
...styles.nameInput,
allowFontScaling: false,
maxWidth: textMaxWidth,
})
const textMinWidth = clamp(MeasureText.measureWidth(value.slice(0, displayCharCnt).trimEnd(), {
...styles.nameInput,
allowFontScaling: false,
}), textMaxWidth)
return (
<View style={{ width: "100%", alignItems: "center", backgroundColor: "#444", }}>
<TextInput
style={[styles.nameInput, { minWidth: textMinWidth, maxWidth: textMaxWidth, }]}
value={value}
onChangeText={setValue}
placeholder="?"
placeholderTextColor="white"
numberOfLines={1}
/>
<Text style={styles.nameInput} numberOfLines={1}>{displayCharCnt}</Text>
</View>
)
}
const styles = StyleSheet.create({
nameInput: {
// maxWidth: "75%",
color: "white",
textAlign: "center",
fontSize: 30,
minWidth: 20,
},
label: {
color: "white",
textAlign: "center",
fontSize: 30,
maxWidth: textMaxWidth
},
})
function clamp(value, max) {
return value >= max ? max : value
}
I needed to measure text to prevent a TextInput from determining the location of the text break itself, then resizing. I wanted the TextInput to fit it's content into a maximum width, so I used a patch to this package to calculate what needed to be the minimum width of the TextInput. I thought I would share since I never found any solutions to this problem online and it being a likely valuable feature to this package.
To see how what this solution solves, center a TextInput with a maxWidth. (I used a percentage amount.) The TextInput will change it's width depending on the first optimal text break found for that maxWidth of the TextInput. I never found a way to disable this behavior, so my solution to this was a work around.
My patch:
Usage: