Реализация Swift 4, вдохновленная ответом @Jane Sales.
При расчете доступной ширины и высоты мы также должны учитывать возможные вертикальные и горизонтальные поля (textContainerInset
и textContainer.lineFragmentPadding
).
Вот лучшее объяснение того, как работают поля UITextView
: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextUILayer/Tasks/SetTextMargins.html
Если текстовое представление может изменить размер, то мы также должны принудительно настроить макет, чтобы мы могли рассчитать размер шрифта на основе максимально возможного размера текстового представления. В этом случае учитывается только высота (макеты только в том случае, если требуемая высота текста больше исходной доступной высоты).
import UIKit
extension UITextView {
func adjustFontToFitText(minimumScale: CGFloat) {
guard let font = font else {
return
}
let scale = max(0.0, min(1.0, minimumScale))
let minimumFontSize = font.pointSize * scale
adjustFontToFitText(minimumFontSize: minimumFontSize)
}
func adjustFontToFitText(minimumFontSize: CGFloat) {
guard let font = font, minimumFontSize > 0.0 else {
return
}
let minimumSize = floor(minimumFontSize)
var fontSize = font.pointSize
let availableWidth = bounds.width - (textContainerInset.left + textContainerInset.right) - (2 * textContainer.lineFragmentPadding)
var availableHeight = bounds.height - (textContainerInset.top + textContainerInset.bottom)
let boundingSize = CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)
var height = text.boundingRect(with: boundingSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).height
if height > availableHeight {
// If text view can vertically resize than we want to get the maximum possible height
sizeToFit()
layoutIfNeeded()
availableHeight = bounds.height - (textContainerInset.top + textContainerInset.bottom)
}
while height >= availableHeight {
guard fontSize > minimumSize else {
break
}
fontSize -= 1.0
let newFont = font.withSize(fontSize)
height = text.boundingRect(with: boundingSize, options: .usesLineFragmentOrigin, attributes: [.font: newFont], context: nil).height
}
self.font = font.withSize(fontSize)
}
}