Uilabel Wrong Word Wrap in iOS 11

UILabel wrong word wrap in iOS 11

This is a change by Apple to prevent widowed lines. From a design perspective, it is preferred to avoid having a single word on a line of text. UILabel now breaks the line in a way that the second line of text always has at least 2 words on it.

See the answer below for an option to disable it.

Sample Image

Also here's a good article about "widowed" and "orphaned" text.

Word wrap not working in UILabel

Try this. It should shrink the font just enough to fit the longest word on a line. If you see a better way let me know as I would love to improve this.I am sure I could adapt it to work in IB.

import UIKit

extension UILabel{

func adjustedFont()->UIFont {
let attributes: [NSAttributedStringKey: Any] = [.font: font]
let attributedString = NSAttributedString(string: text ?? "", attributes: attributes)
let drawingContext = NSStringDrawingContext()
drawingContext.minimumScaleFactor = minimumScaleFactor

attributedString.boundingRect(with: bounds.integral.size,
options: .usesLineFragmentOrigin,
context: drawingContext)

let fontSize = font.pointSize * drawingContext.actualScaleFactor

return font.withSize(fontSize)
}

func fitMaxWord(){
layoutIfNeeded()
let scaledFont = self.adjustedFont()
let currentFont = scaledFont
if let txt = text,
let maxString = txt.components(separatedBy: " ").max(by: {$1.count > $0.count})?.replacingOccurrences(of: "\n", with: ""){
let maxFontSize: CGFloat = currentFont.pointSize
let minFontSize: CGFloat = 5.0


var q = maxFontSize
var p = minFontSize
let height = currentFont.lineHeight
let constraintSize = CGSize(width: .greatestFiniteMagnitude, height: height)
var sizedFont = currentFont
while(p <= q){
let currentSize = (p + q) / CGFloat(2)
sizedFont = currentFont.withSize( CGFloat(currentSize) )

let text = NSMutableAttributedString(string:maxString, attributes:[NSAttributedStringKey.font:sizedFont])

let textRect = text.boundingRect(with: constraintSize, options: [.usesLineFragmentOrigin], context: nil).integral
let labelSize = textRect.width

//1 is a fudge factor
if labelSize == ceil(self.bounds.width - 1){
break
}else if labelSize > ceil(self.bounds.width - 1){
q = currentSize - 0.1
}else{
p = currentSize + 0.1
}
}

if sizedFont.pointSize < currentFont.pointSize{
self.font = sizedFont
}
}
}
}

Minimum example:

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

//we could test longer text
//
let text = "CHULALONGKORN UNIVERSITY Plus a lot of additional text to make sure it still does not clip a word"
let testLabel = UILabel(frame: .zero)
//testLabel.text = "CHULALONGKORN UNIVERSITY"
testLabel.text = text
testLabel.numberOfLines = 0
testLabel.textAlignment = .center
testLabel.font = UIFont.systemFont(ofSize: 45)
testLabel.adjustsFontSizeToFitWidth = true //unneeded in this instance but lets set it
testLabel.minimumScaleFactor = 0.3
self.view.addSubview(testLabel)
testLabel.translatesAutoresizingMaskIntoConstraints = false
testLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
testLabel.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
testLabel.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50).isActive = true
self.view.layoutIfNeeded()
testLabel.fitMaxWord()
}


}

ExampleImage
This should work in most circumstances. I tested with your text and more text. You can see both in the example above.

UILabel Text Not Wrapping

First, the good news: You have set the label to 2 lines and Word Wrap. So it can wrap. Excellent.

Now you must make sure the label is tall enough. Either give it no height constraint, or give it a big enough height constraint that it can accommodate two lines.

Finally, you must limit its width. This is what causes the text to wrap. If you don't limit the label's width, it will just keep growing rightward, potentially continuing off the screen. The limit on the label's width stops this rightward growth and causes the text to wrap (and the label to grow downward instead).

You can limit width in several ways. You can have an actual width constraints. Or you can have a leading constraint and a trailing constraint, to something relatively immovable, such as the superview. And there is a third way: on the Size inspector (which you do also show, at the bottom right of your question), set the Preferred Width (it is shown at the top of the Size inspector): this is the width at which, all other things being equal, the label will stop growing to the right and wrap and grow down instead.

UILabel word wrap feature leaving space even when sufficient space available for the word

You may want to give this a try...

Subclass UITextView, disable scrolling, editing and selecting... set the textContainerInset = UIEdgeInsets.zero and textContainer.lineFragmentPadding = 0 to Zero.

Result:

Sample Image

Code (@IBDesignable so we can see it in IB / Storyboard):

@IBDesignable
class TextViewLabel: UITextView {

override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

func commonInit() -> Void {
isScrollEnabled = false
isEditable = false
isSelectable = false
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
}

}

How to wrap text UILabel Xcode with Storyboard settings

You need to add leading and trailing constraint to your label

Sample Image

Or you can add a width constraint relative to the superview width

Sample Image



Related Topics



Leave a reply



Submit