How would I set the font size of text in a UITextView such that it fills the entire UITextView? I'd like the user to type in their text, then have the text fill the entire UITextView.
Any help is appreciated!
How would I set the font size of text in a UITextView such that it fills the entire UITextView? I'd like the user to type in their text, then have the text fill the entire UITextView.
Any help is appreciated!
I have converted dementiazz's answer to Swift:
func updateTextFont() {
if (textView.text.isEmpty || CGSizeEqualToSize(textView.bounds.size, CGSizeZero)) {
return;
}
let textViewSize = textView.frame.size;
let fixedWidth = textViewSize.width;
let expectSize = textView.sizeThatFits(CGSizeMake(fixedWidth, CGFloat(MAXFLOAT)));
var expectFont = textView.font;
if (expectSize.height > textViewSize.height) {
while (textView.sizeThatFits(CGSizeMake(fixedWidth, CGFloat(MAXFLOAT))).height > textViewSize.height) {
expectFont = textView.font!.fontWithSize(textView.font!.pointSize - 1)
textView.font = expectFont
}
}
else {
while (textView.sizeThatFits(CGSizeMake(fixedWidth, CGFloat(MAXFLOAT))).height < textViewSize.height) {
expectFont = textView.font;
textView.font = textView.font!.fontWithSize(textView.font!.pointSize + 1)
}
textView.font = expectFont;
}
}
This, in a category on UITextView, should do the trick. For why you need the fudgefactor, see this post
#define kMaxFieldHeight 9999
-(BOOL)sizeFontToFit:(NSString*)aString minSize:(float)aMinFontSize maxSize:(float)aMaxFontSize
{
float fudgeFactor = 16.0;
float fontSize = aMaxFontSize;
self.font = [self.font fontWithSize:fontSize];
CGSize tallerSize = CGSizeMake(self.frame.size.width-fudgeFactor,kMaxFieldHeight);
CGSize stringSize = [aString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
while (stringSize.height >= self.frame.size.height)
{
if (fontSize <= aMinFontSize) // it just won't fit
return NO;
fontSize -= 1.0;
self.font = [self.font fontWithSize:fontSize];
tallerSize = CGSizeMake(self.frame.size.width-fudgeFactor,kMaxFieldHeight);
stringSize = [aString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
}
return YES;
}
Similar approach to Arie Litovsky's answer but without sub-classing (or use of a category) and not using contentSize
which didn't return the correct height of the rendered text for me. Tested on iOS 7:
while (((CGSize) [_textView sizeThatFits:_textView.frame.size]).height > _textView.frame.size.height) {
_textView.font = [_textView.font fontWithSize:_textView.font.pointSize-1];
}
The approach is to keep reducing the font size until the text just fits inside the frame of the text view.
If you were going to use this in production you would still need to:
Try this, it's a lot simpler:
while (self.contentSize.height > self.frame.size.height)
{
self.font = [self.font fontWithSize:self.font.pointSize -1];
}
Here is my sample code. Basically, I check the expect size to know if font size need to increase or decrease. You need to add UITextViewDelegate to your class to make sure it workings
- (void)updateTextFont:(UITextView *)textView
{
// Only run if has text, otherwise it will make infinity loop
if (textView.text.length == 0 || CGSizeEqualToSize(textView.bounds.size, CGSizeZero)) return;
/*
- Update textView font size
If expectHeight > textViewHeight => descrease font size n point until it reach textViewHeight
If expectHeight < textViewHeight => inscrease font size n point until it reach textViewHeight
*/
CGSize textViewSize = textView.frame.size;
CGFloat fixedWidth = textViewSize.width;
CGSize expectSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
UIFont *expectFont = textView.font;
if (expectSize.height > textViewSize.height) {
while ([textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)].height > textViewSize.height) {
expectFont = [textView.font fontWithSize:(textView.font.pointSize-1)];
textView.font = expectFont;
}
} else {
while ([textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)].height < textViewSize.height) {
expectFont = textView.font;
textView.font = [textView.font fontWithSize:(textView.font.pointSize+1)];
}
textView.font = expectFont;
}
}
This property is only available on UITextFields. To do this in a UITextView, you'd have to constantly watch the text and manually adjust the font size as that changed.
I have converted dementiazz's & Matt Frear's answer to Swift 3:
func updateTextFont() {
if (textView.text.isEmpty || textView.bounds.size.equalTo(CGSize.zero)) {
return
}
let textViewSize = textView.frame.size
let fixedWidth = textViewSize.width
let expectSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT)))
var expectFont = textView.font
if (expectSize.height > textViewSize.height) {
while (textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT))).height > textViewSize.height) {
expectFont = textView.font!.withSize(textView.font!.pointSize - 1)
textView.font = expectFont
}
}
else {
while (textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT))).height < textViewSize.height) {
expectFont = textView.font
textView.font = textView.font!.withSize(textView.font!.pointSize + 1)
}
textView.font = expectFont
}
}
for swift 3
func updateTextFont(textView : UITextView) {
if (textView.text.isEmpty || textView.bounds.size.equalTo(CGSize.zero)) {
return;
}
let textViewSize = textView.frame.size;
let fixedWidth = textViewSize.width;
let expectSize = textView.sizeThatFits(CGSize(width : fixedWidth, height : CGFloat(MAXFLOAT)));
var expectFont = textView.font;
if (expectSize.height > textViewSize.height) {
while (textView.sizeThatFits(CGSize(width : fixedWidth, height : CGFloat(MAXFLOAT))).height > textViewSize.height) {
expectFont = textView.font!.withSize(textView.font!.pointSize - 1)
textView.font = expectFont
}
}
else {
while (textView.sizeThatFits(CGSize(width : fixedWidth,height : CGFloat(MAXFLOAT))).height < textViewSize.height) {
expectFont = textView.font;
textView.font = textView.font!.withSize(textView.font!.pointSize + 1)
}
textView.font = expectFont;
}
}
I have converted Matt Frear's answer to Swift 4.1 as extension for UITextView:
extension UITextView {
func updateTextFont() {
if (self.text.isEmpty || self.bounds.size.equalTo(CGSize.zero)) {
return;
}
let textViewSize = self.frame.size;
let fixedWidth = textViewSize.width;
let expectSize = self.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT)))
var expectFont = self.font
if (expectSize.height > textViewSize.height) {
while (self.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT))).height > textViewSize.height) {
expectFont = self.font!.withSize(self.font!.pointSize - 1)
self.font = expectFont
}
}
else {
while (self.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT))).height < textViewSize.height) {
expectFont = self.font
self.font = self.font!.withSize(self.font!.pointSize + 1)
}
self.font = expectFont
}
}
}
An updated version of @Arie Litovsky's code. This works in iOS7 and later and stretches the font size both up and down so the text fits.
- (void) stretchFontToFit:(UITextView*)textView
{
while( textView.contentSize.height < textView.frame.size.height )
{
textView.font = [textView.font fontWithSize:textView.font.pointSize +1];
[textView layoutIfNeeded];
}
while( textView.contentSize.height > textView.frame.size.height )
{
textView.font = [textView.font fontWithSize:textView.font.pointSize -1];
[textView layoutIfNeeded];
}
}
In viewDidLoad:
textView.textContainer.lineFragmentPadding = 0;
textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
You need to add UITextViewDelegate:
- (void)updateTextFont:(UITextView *)textView {
CGSize textViewSize = textView.frame.size;
CGSize sizeOfText = [textView.text sizeWithAttributes:@{NSFontAttributeName: textView.font}];
CGFloat fontOfWidth = floorf(textView.font.pointSize / sizeOfText.height * textViewSize.height);
CGFloat fontOfHeight = floorf(textView.font.pointSize / sizeOfText.width * textViewSize.width);
textView.font = [textView.font fontWithSize:MIN(fontOfHeight, fontOfWidth)];
}
U can easily do this by using
self.textview.font
= self.textview.font = UIFont(name: self.textview.font!.fontName,
size: self.textview.frame.size.height / 10)!
divide textview.frame.size.height upon some constant based on your requirement..
Here is a solution for Swift 4 (based on Arie Litovsky answer), you can put this code into textViewDidChange(_ textView: UITextView)
delegate method :
Scaling down the font :
while (textView.contentSize.height > textView.frame.size.height) {
guard let fontName = textView.font?.fontName, let fontSize = textView.font?.pointSize else {return}
if fontSize < 12 {
return
}
textView.font = UIFont(name: fontName, size: fontSize - 1)
textView.layoutIfNeeded()
}
And scaling up the font:
while (textView.contentSize.height < textView.frame.size.height) {
guard let fontName = textView.font?.fontName, let fontSize = textView.font?.pointSize else {return}
if fontSize > 15 {
return
}
textView.font = UIFont(name: fontName, size: fontSize + 1)
textView.layoutIfNeeded()
}
You can change the fontSize < 12
and fontSize > 15
to fit your needs in minimum and maximum font size.