I tried to make an editable uitextview with centered text that can have a maximum of 2 lines and that automatically adjusts its font size in order to fit its text within a fixed width and height.
My solution: Type some text in a UITextView, automatically copy that text and paste it in a uilabel that itself automatically adjusts its font size perfectly, and then retrieve the newly adjusted font size of the uilabel and set that size on the UITextView text.
I have spent about a month on this and repeatedly failed. I can't find a way to make it work. My attempted textview below glitches some letters out and hides large portions of out-of-bounds text instead of resizing everything. Please help me stack overflow Gods.
My attempt:
import UIKit
class TextViewViewController: UIViewController{
private let editableUITextView: UITextView = {
let tv = UITextView()
tv.font = UIFont.systemFont(ofSize: 20)
tv.text = "Delete red text, and type here"
tv.backgroundColor = .clear
tv.textAlignment = .center
tv.textContainer.maximumNumberOfLines = 2
tv.textContainer.lineBreakMode = .byWordWrapping
tv.textColor = .red
return tv
}()
private let correctTextSizeLabel: UILabel = {
let tv = UILabel()
tv.font = UIFont.systemFont(ofSize: 20)
tv.backgroundColor = .clear
tv.text = "This is properly resized"
tv.adjustsFontSizeToFitWidth = true
tv.lineBreakMode = .byTruncatingTail
tv.numberOfLines = 2
tv.textAlignment = .center
tv.textColor = .green
return tv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
view.addSubview(correctTextSizeLabel)
view.addSubview(editableUITextView)
editableUITextView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
editableUITextView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
editableUITextView.heightAnchor.constraint(equalToConstant: 150).isActive = true
editableUITextView.widthAnchor.constraint(equalToConstant: 150).isActive = true
editableUITextView.translatesAutoresizingMaskIntoConstraints = false
editableUITextView.delegate = self
correctTextSizeLabel.leftAnchor.constraint(equalTo: editableUITextView.leftAnchor).isActive = true
correctTextSizeLabel.rightAnchor.constraint(equalTo: editableUITextView.rightAnchor).isActive = true
correctTextSizeLabel.topAnchor.constraint(equalTo: editableUITextView.topAnchor).isActive = true
correctTextSizeLabel.bottomAnchor.constraint(equalTo: editableUITextView.bottomAnchor).isActive = true
correctTextSizeLabel.translatesAutoresizingMaskIntoConstraints = false
editableUITextView.isScrollEnabled = false
}
func getApproximateAdjustedFontSizeOfLabel(label: UILabel) -> CGFloat {
if label.adjustsFontSizeToFitWidth == true {
var currentFont: UIFont = label.font
let originalFontSize = currentFont.pointSize
var currentSize: CGSize = (label.text! as NSString).size(withAttributes: [NSAttributedString.Key.font: currentFont])
while currentSize.width > label.frame.size.width * 2 && currentFont.pointSize > (originalFontSize * label.minimumScaleFactor) {
currentFont = currentFont.withSize(currentFont.pointSize - 1)
currentSize = (label.text! as NSString).size(withAttributes: [NSAttributedString.Key.font: currentFont])
}
return currentFont.pointSize
} else {
return label.font.pointSize
}
}
}
//MARK: - UITextViewDelegate
extension TextViewViewController : UITextViewDelegate {
private func textViewShouldBeginEditing(_ textView: UITextView) {
textView.becomeFirstResponder()
}
func textViewDidBeginEditing(_ textView: UITextView) {
}
func textViewDidEndEditing(_ textView: UITextView) {
}
func textViewDidChange(_ textView: UITextView) {
textView.becomeFirstResponder()
self.correctTextSizeLabel.text = textView.text
self.correctTextSizeLabel.isHidden = false
let estimatedTextSize = self.getApproximateAdjustedFontSizeOfLabel(label: self.correctTextSizeLabel)
print("estimatedTextSize: ",estimatedTextSize)
self.editableUITextView.font = UIFont.systemFont(ofSize: estimatedTextSize)
}
}
UITextField's have the option to automatically adjust font size to fit a fixed width but they only allow 1 line of text, I need it to have a maximum of 2. UILabel's solve this problem perfectly but they aren't editable.