ios - Autorretracción en una UILabel con múltiples líneas.
ios5 (16)
Aquí está la solución de la categoría actualizada para iOS 7 basada en las actualizaciones de itecedor para iOS 6.
Archivo de cabecera:
#import <UIKit/UIKit.h>
@interface UILabel (MultiLineAutoSize)
- (void)adjustFontSizeToFit;
@end
Y el archivo de implementación:
@implementation UILabel (MultiLineAutoSize)
- (void)adjustFontSizeToFit {
UIFont *font = self.font;
CGSize size = self.frame.size;
for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f)
{
font = [font fontWithSize:maxSize];
CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
CGRect textRect = [self.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:font}
context:nil];
CGSize labelSize = textRect.size;
if(labelSize.height <= size.height)
{
self.font = font;
[self setNeedsLayout];
break;
}
}
// set the font to the minimum size anyway
self.font = font;
[self setNeedsLayout]; }
@end
¿Es posible usar la propiedad autoshrink en conjunto en varias líneas en un UILabel
? por ejemplo, el tamaño de texto grande posible en 2 líneas disponibles.
Encontré este enlace http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/
El problema se puede resolver utilizando el Generador de Interfaz en 3 simples pasos:
- Establezca "Autoshrink" en "Tamaño de fuente mínimo".
- Establezca la fuente en el tamaño de fuente más grande que desee (20) y establezca Líneas en, digamos, 10, que en mi caso eran tantas líneas como cabrían en la etiqueta en ese tamaño de fuente.
- Luego, cambie "Saltos de línea" de "Ajuste de palabra" a "Truncar cola".
¡Espero eso ayude!
Escribí una pequeña categoría en UILabel basada en la respuesta de "The Dude''s" para lograr esta funcionalidad.
Estas personas encontraron una solución:
http://www.11pixel.com/blog/28/resize-multi-line-text-to-fit-uilabel-on-iphone/
Su solución es la siguiente:
int maxDesiredFontSize = 28;
int minFontSize = 10;
CGFloat labelWidth = 260.0f;
CGFloat labelRequiredHeight = 180.0f;
//Create a string with the text we want to display.
self.ourText = @"This is your variable-length string. Assign it any way you want!";
/* This is where we define the ideal font that the Label wants to use.
Use the font you want to use and the largest font size you want to use. */
UIFont *font = [UIFont fontWithName:@"Marker Felt" size:maxDesiredFontSize];
int i;
/* Time to calculate the needed font size.
This for loop starts at the largest font size, and decreases by two point sizes (i=i-2)
Until it either hits a size that will fit or hits the minimum size we want to allow (i > 10) */
for(i = maxDesiredFontSize; i > minFontSize; i=i-2)
{
// Set the new font size.
font = [font fontWithSize:i];
// You can log the size you''re trying: NSLog(@"Trying size: %u", i);
/* This step is important: We make a constraint box
using only the fixed WIDTH of the UILabel. The height will
be checked later. */
CGSize constraintSize = CGSizeMake(labelWidth, MAXFLOAT);
// This step checks how tall the label would be with the desired font.
CGSize labelSize = [self.ourText sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
/* Here is where you use the height requirement!
Set the value in the if statement to the height of your UILabel
If the label fits into your required height, it will break the loop
and use that font size. */
if(labelSize.height <= labelRequiredHeight)
break;
}
// You can see what size the function is using by outputting: NSLog(@"Best size is: %u", i);
// Set the UILabel''s font to the newly adjusted font.
msg.font = font;
// Put the text into the UILabel outlet variable.
msg.text = self.ourText;
Para que esto funcione, se debe asignar un IBOutlet en el generador de interfaz a la UILabel.
"IBOutlet UILabel * msg;"
Todo el mérito es de las personas en 11pixel.
Gracias a DaGaMs por esta solución.
Lo he actualizado como sigue:
1 - Para trabajar con iOS 6 (ya que minimalFontSize y UILineBreakModeWordWordWrap están en desuso) 2 - Para eliminar los espacios en blanco del texto de la etiqueta, ya que causará que el cambio de tamaño falle (no querrá saber cuánto tiempo tardé en encontrarlo). insecto)
-(void)adjustFontSizeToFit
{
self.text = [self.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
UIFont *font = self.font;
CGSize size = self.frame.size;
for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor; maxSize -= 1.f)
{
font = [font fontWithSize:maxSize];
CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
if(labelSize.height <= size.height)
{
self.font = font;
[self setNeedsLayout];
break;
}
}
// set the font to the minimum size anyway
self.font = font;
[self setNeedsLayout];
}
Hay un método en NSString
, -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:
que aparentemente ha existido desde iOS 2.0, pero desafortunadamente está en desuso en iOS 7 sin una alternativa sugerida, ya que no se -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:
la reducción automática del tamaño de fuente. No entiendo realmente la postura de Apple sobre esto, ya que lo utilizan en notas clave, etc., y creo que si los tamaños de fuente están dentro de un rango pequeño, está bien. Aquí hay una implementación en Swift usando este método.
var newFontSize: CGFloat = 30
let font = UIFont.systemFontOfSize(newFontSize)
(self.label.text as NSString).sizeWithFont(font, minFontSize: 20, actualFontSize: &newFontSize, forWidth: self.label.frame.size.width, lineBreakMode: NSLineBreakMode.ByWordWrapping)
self.label.font = font.fontWithSize(newFontSize)
No estoy al tanto de cómo se puede lograr esto sin utilizar métodos en desuso.
La respuesta de itedcedor tiene un problema que pwightman señaló. Además, no hay necesidad de recortar espacios en blanco. Aquí está la versión modificada:
- (void)adjustFontSizeToFit {
UIFont *font = self.font;
CGSize size = self.frame.size;
for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) {
font = [font fontWithSize:maxSize];
CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
if(labelSize.height <= size.height) {
self.font = font;
[self setNeedsLayout];
break;
}
}
// set the font to the minimum size anyway
self.font = font;
[self setNeedsLayout];
}
La respuesta marcada como la solución es hacky e imprecisa. UILabel lo manejará automáticamente si establece las siguientes propiedades correctamente:
numberOfLines
debe ser distinto de cero
adjustsFontSizeToFitWidth
debe ser YES
lineBreakMode
no debe ser NSLineBreakByCharWrapping
o NSLineBreakByWordWrapping
Me gustó la respuesta de DaGaMs, pero al usar etiquetas como en UITableViewCells que podrían devolverse de dequeueReusableCell :, el tamaño de fuente regular continuaría reduciéndose incluso cuando el tamaño de fuente original todavía se deseaba para algunas celdas de TableView que tenían menos texto y podían aprovechar El tamaño de fuente original de la etiqueta original.
así que, comenzando con la categoría de DaGaMs como punto de partida, creé una clase separada en lugar de una categoría separada, y me aseguro de que mis UILabels en mi storyboard utilicen esta nueva clase:
#import "MultiLineAutoShrinkLabel.h"
@interface MultiLineAutoShrinkLabel ()
@property (readonly, nonatomic) UIFont* originalFont;
@end
@implementation MultiLineAutoShrinkLabel
@synthesize originalFont = _originalFont;
- (UIFont*)originalFont { return _originalFont ? _originalFont : (_originalFont = self.font); }
- (void)quoteAutoshrinkUnquote
{
UIFont* font = self.originalFont;
CGSize frameSize = self.frame.size;
CGFloat testFontSize = _originalFont.pointSize;
for (; testFontSize >= self.minimumFontSize; testFontSize -= 0.5)
{
CGSize constraintSize = CGSizeMake(frameSize.width, MAXFLOAT);
CGSize testFrameSize = [self.text sizeWithFont:(font = [font fontWithSize:testFontSize])
constrainedToSize:constraintSize
lineBreakMode:self.lineBreakMode];
// the ratio of testFontSize to original font-size sort of accounts for number of lines
if (testFrameSize.height <= frameSize.height * (testFontSize/_originalFont.pointSize))
break;
}
self.font = font;
[self setNeedsLayout];
}
@end
No puedo comentar la publicación de MontiRabbit debido a la falta de reputación, así que haré una nueva respuesta. La solución que él (y su referente) propuso no funciona en Xcode 7.3 o mejor, es impreciso. Para hacerlo funcionar, en el guión gráfico, tuve que:
- Establecer una restricción de ancho (ancho puro o cola y cable)
- CONFIGURAR una RESTRICCIÓN DE ALTURA (esto es muy importante, normalmente con autorizar uno no establece la altura de la etiqueta)
- Establezca la propiedad "Autoshrink" en "Escala de fuente mínima" o "Tamaño de fuente mínimo" (funciona en ambos casos)
- Establezca la propiedad "Saltos de línea" en "Truncar cola"
- Establecer la propiedad "Líneas" en un valor distinto de cero
¡Espero eso ayude! ;)
Para UIButton, solo estas líneas están funcionando para mí:
self.centerBtn.titleLabel.numberOfLines = 2;
self.centerBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
self.centerBtn.titleLabel.adjustsFontSizeToFitWidth = YES;
Prueba esto:
O bien subclase UILabel o llame al método adjustFontSize después de configurar la propiedad de texto en una etiqueta
override var text : String? { didSet { self.adjustFontSize() } }
func adjustFontSize()
{
var lineCount = self.string.components(separatedBy: "/n").count - 1
var textArray = self.string.components(separatedBy: " ")
var wordsToCompare = 1
while(textArray.count > 0)
{
let words = textArray.first(n: wordsToCompare).joined(separator: " ")
let wordsWidth = words.widthForHeight(0, font: self.font)
if(wordsWidth > self.frame.width)
{
textArray.removeFirst(wordsToCompare)
lineCount += 1
wordsToCompare = 1
}
else if(wordsToCompare > textArray.count)
{
break
}
else
{
wordsToCompare += 1
}
}
self.numberOfLines = lineCount + 1
}
Una versión ágil adaptada de @DaGaMs.
SWIFT 2:
extension UILabel {
func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
let maxFontSize = maximumFontSize ?? font.pointSize
for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
let proposedFont = font.fontWithSize(size)
let constraintSize = CGSizeMake(bounds.size.width, CGFloat(MAXFLOAT))
let labelSize = ((text ?? "") as NSString).boundingRectWithSize(constraintSize,
options: .UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: proposedFont],
context: nil)
if labelSize.height <= bounds.size.height {
font = proposedFont
setNeedsLayout()
break;
}
}
}
}
SWIFT 3:
extension UILabel {
func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
let maxFontSize = maximumFontSize ?? font.pointSize
for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
let proposedFont = font.withSize(size)
let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT))
let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize,
options: .usesLineFragmentOrigin,
attributes: [NSFontAttributeName: proposedFont],
context: nil)
if labelSize.height <= bounds.size.height {
font = proposedFont
setNeedsLayout()
break;
}
}
}
}
Utilicé la solución Swift 3 de @ wbarksdale, pero descubrí que las palabras largas se estaban truncando en el medio. Para mantener las palabras intactas, tuve que modificar como se muestra:
extension UILabel {
func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
let maxFontSize = maximumFontSize ?? font.pointSize
let words = self.text?.components(separatedBy: " ")
var longestWord: String?
if let max = words?.max(by: {$1.characters.count > $0.characters.count}) {
longestWord = max
}
for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
let proposedFont = font.withSize(size)
let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT))
let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize,
options: .usesLineFragmentOrigin,
attributes: [NSFontAttributeName: proposedFont],
context: nil)
let wordConstraintSize = CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT))
let longestWordSize = ((longestWord ?? "") as NSString).boundingRect(with: wordConstraintSize,
options: .usesFontLeading,
attributes: [NSFontAttributeName: proposedFont],
context: nil)
if labelSize.height <= bounds.size.height && longestWordSize.width < constraintSize.width {
font = proposedFont
setNeedsLayout()
break
}
}
}
}
UILabel
el código anterior para convertirlo en una categoría en UILabel
:
Archivo de cabecera:
#import <UIKit/UIKit.h>
@interface UILabel (MultiLineAutoSize)
- (void)adjustFontSizeToFit;
@end
Y el archivo de implementación:
@implementation UILabel (MultiLineAutoSize)
- (void)adjustFontSizeToFit
{
UIFont *font = self.font;
CGSize size = self.frame.size;
for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumFontSize; maxSize -= 1.f)
{
font = [font fontWithSize:maxSize];
CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
if(labelSize.height <= size.height)
{
self.font = font;
[self setNeedsLayout];
break;
}
}
// set the font to the minimum size anyway
self.font = font;
[self setNeedsLayout];
}
@end
extension UILabel{
func adjustFont(minSize:Int, maxSize:Int){
var newFont = self.font
for index in stride(from: maxSize, to: minSize, by: -1) {
newFont = UIFont.systemFont(ofSize: CGFloat(index))
let size = CGSize(width: self.frame.width, height: CGFloat(Int.max))
let size2 = (self.text! as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font:newFont!], context: nil).size
if size2.height < self.frame.size.height{
break
}
}
self.font = newFont
}
}
también debe asignar un valor a la propiedad numberOfLines de UILabel.