tipo - Calcular el tamaño de fuente para ajustar el marco-Texto central-NSAttributedString-iOS
propiedades letra html (12)
Aún más fácil / más rápido (pero por supuesto aproximado) sería esta:
class func calculateOptimalFontSize(textLength:CGFloat, boundingBox:CGRect) -> CGFloat
{
let area:CGFloat = boundingBox.width * boundingBox.height
return sqrt(area / textLength)
}
Asumimos que cada carácter es N x N píxeles, por lo que calculamos cuántas veces N x N va dentro del cuadro delimitador.
Tengo un texto que estoy dibujando en un marco fijo a través de una NSAttributedString (código a continuación). En este momento estoy codificando el tamaño del texto a 16. Mi pregunta es, ¿hay una manera de calcular el mejor tamaño de ajuste para el texto para el marco dado?
- (void)drawText:(CGContextRef)contextP startX:(float)x startY:(float)
y withText:(NSString *)standString
{
CGContextTranslateCTM(contextP, 0, (bottom-top)*2);
CGContextScaleCTM(contextP, 1.0, -1.0);
CGRect frameText = CGRectMake(1, 0, (right-left)*2, (bottom-top)*2);
NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:standString];
[attrString addAttribute:NSFontAttributeName
value:[UIFont fontWithName:@"Helvetica-Bold" size:16.0]
range:NSMakeRange(0, attrString.length)];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString));
struct CGPath * p = CGPathCreateMutable();
CGPathAddRect(p, NULL, frameText);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), p, NULL);
CTFrameDraw(frame, contextP);
}
Aquí está el código que hará exactamente eso: calcular el tamaño de fuente óptimo dentro de algunos límites. Esta muestra está en el contexto de la subclase UITextView
, por lo que utiliza sus límites como un "marco dado":
func binarySearchOptimalFontSize(min: Int, max: Int) -> Int {
let middleSize = (min + max) / 2
if min > max {
return middleSize
}
let middleFont = UIFont(name: font!.fontName, size: CGFloat(middleSize))!
let attributes = [NSFontAttributeName : middleFont]
let attributedString = NSAttributedString(string: text, attributes: attributes)
let size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
let textSize = attributedString.boundingRect(with: size, options: options, context: nil)
if textSize.size.equalTo(bounds.size) {
return middleSize
} else if (textSize.height > bounds.size.height || textSize.width > bounds.size.width) {
return binarySearchOptimalFontSize(min: min, max: middleSize - 1)
} else {
return binarySearchOptimalFontSize(min: middleSize + 1, max: max)
}
}
Espero que eso ayude.
Aquí está mi solución en Swift 4:
private func adjustedFontSizeOf(label: UILabel) -> CGFloat {
guard let textSize = label.text?.size(withAttributes: [.font: label.font]), textSize.width > label.bounds.width else {
return label.font.pointSize
}
let scale = label.bounds.width / textSize.width
let actualFontSize = scale * label.font.pointSize
return actualFontSize
}
Espero que esto ayude a alguien.
Aquí hay un método que parece funcionar bien para iOS 9 usando objetos UITextView
. Es posible que tengas que twittearlo un poco para otras aplicaciones.
/*!
* Find the height of the smallest rectangle that will enclose a string using the given font.
*
* @param string The string to check.
* @param font The drawing font.
* @param width The width of the drawing area.
*
* @return The height of the rectngle enclosing the text.
*/
- (float) heightForText: (NSString *) string font: (UIFont *) font width: (float) width {
NSDictionary *fontAttributes = [NSDictionary dictionaryWithObject: font
forKey: NSFontAttributeName];
CGRect rect = [string boundingRectWithSize: CGSizeMake(width, INT_MAX)
options: NSStringDrawingUsesLineFragmentOrigin
attributes: fontAttributes
context: nil];
return rect.size.height;
}
/*!
* Find the largest font size that will allow a block of text to fit in a rectangle of the given size using the system
* font.
*
* The code is tested and optimized for UITextView objects.
*
* The font size is determined to ±0.5. Change delta in the code to get more or less precise results.
*
* @param string The string to check.
* @param size The size of the bounding rectangle.
*
* @return: The font size.
*/
- (float) maximumSystemFontSize: (NSString *) string size: (CGSize) size {
// Hack: For UITextView, the last line is clipped. Make sure it''s not one we care about.
if ([string characterAtIndex: string.length - 1] != ''/n'') {
string = [string stringByAppendingString: @"/n"];
}
string = [string stringByAppendingString: @"M/n"];
float maxFontSize = 16.0;
float maxHeight = [self heightForText: string font: [UIFont systemFontOfSize: maxFontSize] width: size.width];
while (maxHeight < size.height) {
maxFontSize *= 2.0;
maxHeight = [self heightForText: string font: [UIFont systemFontOfSize: maxFontSize] width: size.width];
}
float minFontSize = maxFontSize/2.0;
float minHeight = [self heightForText: string font: [UIFont systemFontOfSize: minFontSize] width: size.width];
while (minHeight > size.height) {
maxFontSize = minFontSize;
minFontSize /= 2.0;
maxHeight = minHeight;
minHeight = [self heightForText: string font: [UIFont systemFontOfSize: minFontSize] width: size.width];
}
const float delta = 0.5;
while (maxFontSize - minFontSize > delta) {
float middleFontSize = (minFontSize + maxFontSize)/2.0;
float middleHeight = [self heightForText: string font: [UIFont systemFontOfSize: middleFontSize] width: size.width];
if (middleHeight < size.height) {
minFontSize = middleFontSize;
minHeight = middleHeight;
} else {
maxFontSize = middleFontSize;
maxHeight = middleHeight;
}
}
return minFontSize;
}
Aquí hay una pieza simple de código que determinará el tamaño máximo de fuente para que se ajuste a los límites de un marco:
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.text = @"Some text";
float largestFontSize = 12;
while ([label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:largestFontSize]}].width > modifierFrame.size.width)
{
largestFontSize--;
}
label.font = [UIFont systemFontOfSize:largestFontSize];
Este es el código para que el tamaño de fuente dinámico cambie según el ancho del marco, usando la lógica de las otras respuestas. El ciclo while puede ser peligroso, así que no dude en enviar mejoras.
float fontSize = 17.0f; //initial font size
CGSize rect;
while (1) {
fontSize = fontSize+0.1;
rect = [watermarkText sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}];
if ((int)rect.width == (int)subtitle1Text.frame.size.width) {
break;
}
}
subtitle1Text.fontSize = fontSize;
La única forma en que puedo ver que esto sea posible es tener un sistema que ejecute el cálculo de tamaño y luego lo ajuste y lo repita hasta que encuentre el tamaño correcto.
Es decir, configurar un algoritmo de bisección que va entre ciertos tamaños.
Ej. ejecutarlo para el tamaño 10. Demasiado pequeño. Talla 20. Demasiado pequeño. Talla 30. Demasiado grande. Tamaño 25. Demasiado pequeño. Tamaño 27. Justo a la derecha, usa el tamaño 27.
Incluso podrías empezar en cientos.
Talla 100. Demasiado grande. Talla 50. etc ...
La respuesta actualmente aceptada habla de un algoritmo, pero iOS proporciona cálculos para un objeto NSString. Yo usaría sizeWithAttributes:
de la clase NSString
.
sizeWithAttributes:
Devuelve el tamaño del cuadro delimitador que ocupa el receptor cuando se dibuja con los atributos dados.
- (CGSize)sizeWithAttributes:(NSDictionary *)attributes
Fuente: Apple Docs - NSString UIKit Additions Reference
EDITAR malinterpretó la pregunta, por lo que esta respuesta está fuera de lugar.
Me gusta el enfoque dado por @holtwick, pero descubrí que a veces sobreestimaría lo que cabría. Creé un pellizco que parece funcionar bien en mis pruebas. Consejo: No olvides probar con letras realmente anchas como "WWW" o incluso "௵௵௵"
func idealFontSize(for text: String, font: UIFont, width: CGFloat) -> CGFloat {
let baseFontSize = CGFloat(256)
let textSize = text.size(attributes: [NSFontAttributeName: font.withSize(baseFontSize)])
let ratio = width / textSize.width
let ballparkSize = baseFontSize * ratio
let stoppingSize = ballparkSize / CGFloat(2) // We don''t want to loop forever, if we''ve already come down to 50% of the ballpark size give up
var idealSize = ballparkSize
while (idealSize > stoppingSize && text.size(attributes: [NSFontAttributeName: font.withSize(idealSize)]).width > width) {
// We subtract 0.5 because sometimes ballparkSize is an overestimate of a size that will fit
idealSize -= 0.5
}
return idealSize
}
Puede establecer la propiedad de adjustsFontSizeToFitWidth
en YES
según la documentación de Apple
Un pequeño truco ayuda a utilizar sizeWithAttributes:
sin la necesidad de iterar para obtener el resultado correcto:
NSSize sampleSize = [wordString sizeWithAttributes:
@{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }];
CGFloat ratio = rect.size.width / sampleSize.width;
fontSize *= ratio;
Asegúrese de que el fontSize
de fontSize
para la muestra sea lo suficientemente grande como para obtener buenos resultados.
Usted podría usar sizeWithFont:
[myString sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24]
constrainedToSize:CGSizeMake(293, 10000)] // put the size of your frame
Pero está en desuso en iOS 7, así que recomiendo si trabajas con cadenas en UILabel:
[string sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];
Si está trabajando con un rect:
CGRect textRect = [text boundingRectWithSize:mySize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:FONT}
context:nil];
CGSize size = textRect.size;