ios - Multilínea UILabel con ajustesFontSizeToFitWidth
iphone cocoa-touch (4)
Tengo un UILabel de líneas múltiples cuyo tamaño de letra me gustaría ajustar dependiendo de la longitud del texto. El texto completo debe caber en el marco de la etiqueta sin truncarlo.
Desafortunadamente, según la documentación, la propiedad adjustsFontSizeToFitWidth
"solo es efectiva cuando la propiedad numberOfLines
está establecida en 1".
Traté de determinar el tamaño de fuente ajustado usando
-[NSString (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode]
y luego disminuyendo el tamaño de la fuente hasta que encaje. Desafortunadamente, este método trunca internamente el texto para que se ajuste al tamaño especificado y devuelve el tamaño de la cadena truncada resultante.
En esta pregunta , 0x90 proporciona una solución que, aunque un poco fea, hace lo que quiero. Específicamente, trata correctamente la situación de que una sola palabra no se ajusta al ancho en el tamaño de fuente inicial. He modificado ligeramente el código para que funcione como una categoría en NSString
:
- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
CGFloat fontSize = [font pointSize];
CGFloat height = [self sizeWithFont:font constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
UIFont *newFont = font;
//Reduce font size while too large, break if no height (empty string)
while (height > size.height && height != 0) {
fontSize--;
newFont = [UIFont fontWithName:font.fontName size:fontSize];
height = [self sizeWithFont:newFont constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
};
// Loop through words in string and resize to fit
for (NSString *word in [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
CGFloat width = [word sizeWithFont:newFont].width;
while (width > size.width && width != 0) {
fontSize--;
newFont = [UIFont fontWithName:font.fontName size:fontSize];
width = [word sizeWithFont:newFont].width;
}
}
return fontSize;
}
Para usarlo con un UILabel
:
CGFloat fontSize = [label.text fontSizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:label.frame.size];
label.font = [UIFont boldSystemFontOfSize:fontSize];
EDIT : newFont
el código para inicializar newFont
con la font
. Soluciona un bloqueo en ciertas circunstancias.
En algunos casos, cambiar "Saltos de línea" de "Ajustar palabra" a "Truncar cola" puede ser todo lo que necesita, si sabe cuántas líneas desea (por ejemplo, "2"): Crédito: Becky Hansmeyer
Gracias, con eso y un poco más de otra persona, hice este UILabel personalizado, que respetará el tamaño mínimo de fuente y hay una opción de bonificación para alinear el texto a la parte superior.
h:
@interface EPCLabel : UILabel {
float originalPointSize;
CGSize originalSize;
}
@property (nonatomic, readwrite) BOOL alignTextOnTop;
@end
metro:
#import "EPCLabel.h"
@implementation EPCLabel
@synthesize alignTextOnTop;
-(void)verticalAlignTop {
CGSize maximumSize = originalSize;
NSString *dateString = self.text;
UIFont *dateFont = self.font;
CGSize dateStringSize = [dateString sizeWithFont:dateFont
constrainedToSize:CGSizeMake(self.frame.size.width, maximumSize.height)
lineBreakMode:self.lineBreakMode];
CGRect dateFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, dateStringSize.height);
self.frame = dateFrame;
}
- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
CGFloat fontSize = [font pointSize];
CGFloat height = [self.text sizeWithFont:font
constrainedToSize:CGSizeMake(size.width,FLT_MAX)
lineBreakMode:UILineBreakModeWordWrap].height;
UIFont *newFont = font;
//Reduce font size while too large, break if no height (empty string)
while (height > size.height && height != 0 && fontSize > self.minimumFontSize) {
fontSize--;
newFont = [UIFont fontWithName:font.fontName size:fontSize];
height = [self.text sizeWithFont:newFont
constrainedToSize:CGSizeMake(size.width,FLT_MAX)
lineBreakMode:UILineBreakModeWordWrap].height;
};
// Loop through words in string and resize to fit
if (fontSize > self.minimumFontSize) {
for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
CGFloat width = [word sizeWithFont:newFont].width;
while (width > size.width && width != 0 && fontSize > self.minimumFontSize) {
fontSize--;
newFont = [UIFont fontWithName:font.fontName size:fontSize];
width = [word sizeWithFont:newFont].width;
}
}
}
return fontSize;
}
-(void)setText:(NSString *)text {
[super setText:text];
if (originalSize.height == 0) {
originalPointSize = self.font.pointSize;
originalSize = self.frame.size;
}
if (self.adjustsFontSizeToFitWidth && self.numberOfLines > 1) {
UIFont *origFont = [UIFont fontWithName:self.font.fontName size:originalPointSize];
self.font = [UIFont fontWithName:origFont.fontName size:[self fontSizeWithFont:origFont constrainedToSize:originalSize]];
}
if (self.alignTextOnTop) [self verticalAlignTop];
}
-(void)setAlignTextOnTop:(BOOL)flag {
alignTextOnTop = YES;
if (alignTextOnTop && self.text != nil)
[self verticalAlignTop];
}
@end
Espero que ayude.
Hay una extensión ObjC en los comentarios, que calcula el tamaño de fuente requerido para ajustar el texto de líneas múltiples en UILabel. Fue reescrito en Swift (ya que es 2016):
//
// NSString+KBAdditions.swift
//
// Created by Alexander Mayatsky on 16/03/16.
//
// Original code from http://.com/a/4383281/463892 & http://.com/a/18951386
//
import Foundation
import UIKit
protocol NSStringKBAdditions {
func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat
}
extension NSString : NSStringKBAdditions {
func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat {
var fontSize = font.pointSize
let minimumFontSize = fontSize * minimumScaleFactor
var attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: font])
var height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height
var newFont = font
//Reduce font size while too large, break if no height (empty string)
while (height > size.height && height != 0 && fontSize > minimumFontSize) {
fontSize--;
newFont = UIFont(name: font.fontName, size: fontSize)!
attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: newFont])
height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height
}
// Loop through words in string and resize to fit
for word in self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) {
var width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width
while (width > size.width && width != 0 && fontSize > minimumFontSize) {
fontSize--
newFont = UIFont(name: font.fontName, size: fontSize)!
width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width
}
}
return fontSize;
}
}
Enlace al código completo: https://gist.github.com/amayatsky/e6125a2288cc2e4f1bbf