Fix UITextView line height adjustment for DynamicType, always use the max width for the view (#2916)
* fix text being cut off * adjust line height for dynamictypezio/stable
parent
43206d9f57
commit
d2e5f83cd2
|
@ -40,19 +40,19 @@ class RNUITextViewShadow: RCTShadowView {
|
||||||
self.setAttributedText()
|
self.setAttributedText()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell yoga not to use flexbox
|
// Returning true here will tell Yoga to not use flexbox and instead use our custom measure func.
|
||||||
override func isYogaLeafNode() -> Bool {
|
override func isYogaLeafNode() -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only need to insert text children
|
// We should only insert children that are UITextView shadows
|
||||||
override func insertReactSubview(_ subview: RCTShadowView!, at atIndex: Int) {
|
override func insertReactSubview(_ subview: RCTShadowView!, at atIndex: Int) {
|
||||||
if subview.isKind(of: RNUITextViewChildShadow.self) {
|
if subview.isKind(of: RNUITextViewChildShadow.self) {
|
||||||
super.insertReactSubview(subview, at: atIndex)
|
super.insertReactSubview(subview, at: atIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whenever the subvies update, set the text
|
// Every time the subviews change, we need to reformat and render the text.
|
||||||
override func didUpdateReactSubviews() {
|
override func didUpdateReactSubviews() {
|
||||||
self.setAttributedText()
|
self.setAttributedText()
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ class RNUITextViewShadow: RCTShadowView {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the text
|
// Since we are inside the shadow view here, we have to find the real view and update the text.
|
||||||
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
|
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
|
||||||
guard let textView = viewRegistry?[self.reactTag] as? RNUITextView else {
|
guard let textView = viewRegistry?[self.reactTag] as? RNUITextView else {
|
||||||
return
|
return
|
||||||
|
@ -100,18 +100,25 @@ class RNUITextViewShadow: RCTShadowView {
|
||||||
// Create the attributed string with the generic attributes
|
// Create the attributed string with the generic attributes
|
||||||
let string = NSMutableAttributedString(string: child.text, attributes: attributes)
|
let string = NSMutableAttributedString(string: child.text, attributes: attributes)
|
||||||
|
|
||||||
// Set the paragraph style attributes if necessary
|
// Set the paragraph style attributes if necessary. We can check this by seeing if the provided
|
||||||
|
// line height is not 0.0.
|
||||||
let paragraphStyle = NSMutableParagraphStyle()
|
let paragraphStyle = NSMutableParagraphStyle()
|
||||||
if child.lineHeight != 0.0 {
|
if child.lineHeight != 0.0 {
|
||||||
paragraphStyle.minimumLineHeight = child.lineHeight
|
// Whenever we change the line height for the text, we are also removing the DynamicType
|
||||||
paragraphStyle.maximumLineHeight = child.lineHeight
|
// adjustment for line height. We need to get the multiplier and apply that to the
|
||||||
|
// line height.
|
||||||
|
let scaleMultiplier = scaledFontSize / child.fontSize
|
||||||
|
paragraphStyle.minimumLineHeight = child.lineHeight * scaleMultiplier
|
||||||
|
paragraphStyle.maximumLineHeight = child.lineHeight * scaleMultiplier
|
||||||
|
|
||||||
string.addAttribute(
|
string.addAttribute(
|
||||||
NSAttributedString.Key.paragraphStyle,
|
NSAttributedString.Key.paragraphStyle,
|
||||||
value: paragraphStyle,
|
value: paragraphStyle,
|
||||||
range: NSMakeRange(0, string.length)
|
range: NSMakeRange(0, string.length)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store that height
|
// To calcualte the size of the text without creating a new UILabel or UITextView, we have
|
||||||
|
// to store this line height for later.
|
||||||
self.lineHeight = child.lineHeight
|
self.lineHeight = child.lineHeight
|
||||||
} else {
|
} else {
|
||||||
self.lineHeight = font.lineHeight
|
self.lineHeight = font.lineHeight
|
||||||
|
@ -124,24 +131,22 @@ class RNUITextViewShadow: RCTShadowView {
|
||||||
self.dirtyLayout()
|
self.dirtyLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a YGSize based on the max width
|
// To create the needed size we need to:
|
||||||
|
// 1. Get the max size that we can use for the view
|
||||||
|
// 2. Calculate the height of the text based on that max size
|
||||||
|
// 3. Determine how many lines the text is, and limit that number if it exceeds the max
|
||||||
|
// 4. Set the frame size and return the YGSize. YGSize requires Float values while CGSize needs CGFloat
|
||||||
func getNeededSize(maxWidth: Float) -> YGSize {
|
func getNeededSize(maxWidth: Float) -> YGSize {
|
||||||
// Create the max size and figure out the size of the entire text
|
|
||||||
let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT))
|
let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT))
|
||||||
let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
|
let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil)
|
||||||
|
|
||||||
// Figure out how many total lines there are
|
var totalLines = Int(ceil(textSize.height / self.lineHeight))
|
||||||
let totalLines = Int(ceil(textSize.height / self.lineHeight))
|
|
||||||
|
|
||||||
// Default to the text size
|
|
||||||
var neededSize: CGSize = textSize.size
|
|
||||||
|
|
||||||
// If the total lines > max number, return size with the max
|
|
||||||
if self.numberOfLines != 0, totalLines > self.numberOfLines {
|
if self.numberOfLines != 0, totalLines > self.numberOfLines {
|
||||||
neededSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(CGFloat(self.numberOfLines) * self.lineHeight))
|
totalLines = self.numberOfLines
|
||||||
}
|
}
|
||||||
|
|
||||||
self.frameSize = neededSize
|
self.frameSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(CGFloat(totalLines) * self.lineHeight))
|
||||||
return YGSize(width: Float(neededSize.width), height: Float(neededSize.height))
|
return YGSize(width: Float(self.frameSize.width), height: Float(self.frameSize.height))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue