Reemplazo por tamaño obsoleto ConFont: en iOS 7?


320

En iOS 7, sizeWithFont:ahora está en desuso. ¿Cómo paso ahora el objeto UIFont al método de reemplazo sizeWithAttributes:?

Respuestas:


521

Use en su sizeWithAttributes:lugar, que ahora toma un NSDictionary. Pase el par con clave UITextAttributeFonty su objeto de fuente de esta manera:

CGSize size = [string sizeWithAttributes:
    @{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];

// Values are fractional -- you should take the ceilf to get equivalent values
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

6060
al trabajar con una NSStringy una UILabel(no SIEMPRE es el caso, pero a menudo lo es), para evitar códigos duplicados / etc., también puede reemplazar [UIFont systemFontOfSize:17.0f]con label.font: ayuda al mantenimiento del código al hacer referencia a los datos existentes en lugar de escribirlos varias veces o hacer referencia a constantes en todo el lugar, etc.
toblerpwn

8
@toblerpwn es posible que la etiqueta no exista e intente calcular una etiqueta teórica.
botbot

55
¿Cómo usaría este ejemplo para obtener el tamaño.Altura con una etiqueta que tenga un ancho fijo de, por ejemplo, 250? O si es una etiqueta con autolatyout bruja toma una ventaja del ancho y voy al modo horizontal.
Pedroinpeace

12
@Pedroinpeace En su boundingRectWithSize:options:attributes:context:lugar, usaría , pasando CGSizeMake(250.0f, CGFLOAT_MAX)en la mayoría de los casos.
James Kuang

99
Algo más a tener en cuenta sizeWithAttributes: no es 100% equivalente. sizeWithFont solía redondear los tamaños a valores enteros (píxeles). Sugiera usar ceilf en la altura / ancho resultante o puede obtener artefactos borrosos (especialmente no retina HW) si lo usa para cálculos posicionales.
Nick H247

172

Creo que la función fue obsoleta porque esa serie de NSString+UIKitfunciones ( sizewithFont:..., etc.) se basaron en la UIStringDrawingbiblioteca, que no era segura para subprocesos. Si trató de ejecutarlos no en el hilo principal (como cualquier otra UIKitfuncionalidad), obtendrá comportamientos impredecibles. En particular, si ejecutó la función en varios subprocesos simultáneamente, probablemente bloqueará su aplicación. Es por eso que en iOS 6, introdujeron un boundingRectWithSize:...método para NSAttributedString. Esto se creó sobre las NSStringDrawingbibliotecas y es seguro para subprocesos.

Si observa la nueva NSString boundingRectWithSize:...función, solicita una matriz de atributos de la misma manera que a NSAttributeString. Si tuviera que adivinar, esta nueva NSStringfunción en iOS 7 es simplemente un contenedor para la NSAttributeStringfunción de iOS 6.

En esa nota, si solo estuvieras soportando iOS 6 y iOS 7, entonces definitivamente cambiaría todo tu NSString sizeWithFont:...a la NSAttributeString boundingRectWithSize. ¡Le ahorrará mucho dolor de cabeza si tiene un caso de esquina de múltiples hilos extraño! Así es como me convertí NSString sizeWithFont:constrainedToSize::

Lo que solía ser:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:(CGSize){width, CGFLOAT_MAX}];

Se puede reemplazar con:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;

Tenga en cuenta que la documentación menciona:

En iOS 7 y versiones posteriores, este método devuelve tamaños fraccionarios (en el componente de tamaño del devuelto CGRect); para usar un tamaño devuelto para vistas de tamaño, debe usar elevar su valor al entero más alto más cercano usando la función ceil.

Entonces, para extraer la altura o el ancho calculados que se usarán para dimensionar vistas, usaría:

CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);

boundingRectWithSize está en desuso en iOS 6.0.
Nirav

2
@Nirav No veo ninguna mención de la desaprobación. ¿Me puede indicar dónde dice que está en desuso? ¡Gracias! ( developer.apple.com/library/ios/documentation/uikit/reference/… )
Sr. T

2
¿Quizás @Nirav quería decir que no estaba disponible para NSString en iOS 6 (que se menciona indirectamente en la respuesta)?
newenglander

1
@VagueExplanation Acabo de probar eso y boundingRectForSize todavía no está en desuso para NSAttributedString. Tampoco dice obsoleto en la documentación.
Sr. T

55
Fantástico por mencionar el uso de ceilf (). Ningún otro lugar mencionó eso y mi tamaño siempre fue un poco demasiado pequeño. ¡Gracias!
Jonathan Brown

29

Como puede ver sizeWithFonten el sitio para desarrolladores de Apple, está en desuso, por lo que debemos usarlo sizeWithAttributes.

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

NSString *text = @"Hello iOS 7.0";
if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
    // code here for iOS 5.0,6.0 and so on
    CGSize fontSize = [text sizeWithFont:[UIFont fontWithName:@"Helvetica" 
                                                         size:12]];
} else {
    // code here for iOS 7.0
   CGSize fontSize = [text sizeWithAttributes: 
                            @{NSFontAttributeName: 
                              [UIFont fontWithName:@"Helvetica" size:12]}];
}

17
En este caso, es mejor usar el [NSObject respondsToSelector:]método como aquí: stackoverflow.com/a/3863039/1226304
derpoliuk el

16

Creé una categoría para manejar este problema, aquí está:

#import "NSString+StringSizeWithFont.h"

@implementation NSString (StringSizeWithFont)

- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

De esta manera sólo tiene que buscar / reemplazar sizeWithFont:con sizeWithMyFont:y usted es bueno ir.


1
aunque esto producirá una advertencia de compilador en ios7 + para hacer referencia a sizeWithFont, o una advertencia / error de compilación en <ios7 para hacer referencia a sizeWithAttributes! Probablemente sea mejor usar una macro en lugar de verificar responddsToSelector, si es necesario. Pero como ya no puede enviar a Apple con ioS6 SDK ... ¡probablemente no lo sea!
Nick H247

Agregue esto para suprimir la advertencia #pragma El diagnóstico de CCG se ignoró "-Wdeprecated-declaraciones"
Ryan Heitner

10

En iOS7, necesitaba la lógica para devolver la altura correcta para la vista de tabla: heightForRowAtIndexPath, pero sizeWithAttributes siempre devuelve la misma altura independientemente de la longitud de la cadena porque no sabe que se colocará en una celda de tabla de ancho fijo . ¡Encontré que esto funciona muy bien para mí y calcula la altura correcta teniendo en cuenta el ancho de la celda de la tabla! Esto se basa en la respuesta anterior del Sr. T.

NSString *text = @"The text that I want to wrap in a table cell."

CGFloat width = tableView.frame.size.width - 15 - 30 - 15;  //tableView width - left border width - accessory indicator - right border width
UIFont *font = [UIFont systemFontOfSize:17];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;
size.height = ceilf(size.height);
size.width  = ceilf(size.width);
return size.height + 15;  //Add a little more padding for big thumbs and the detailText label

7

Las etiquetas de varias líneas que usan altura dinámica pueden requerir información adicional para establecer el tamaño correctamente. Puede usar sizeWithAttributes con UIFont y NSParagraphStyle para especificar tanto la fuente como el modo de salto de línea.

Definiría el estilo de párrafo y usaría un NSDictionary como este:

// set paragraph style
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByWordWrapping];
// make dictionary of attributes with paragraph style
NSDictionary *sizeAttributes        = @{NSFontAttributeName:myLabel.font, NSParagraphStyleAttributeName: style};
// get the CGSize
CGSize adjustedSize = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);

// alternatively you can also get a CGRect to determine height
CGRect rect = [myLabel.text boundingRectWithSize:adjustedSize
                                                         options:NSStringDrawingUsesLineFragmentOrigin
                                                      attributes:sizeAttributes
                                                         context:nil];

Puede usar CGSize 'ajustadoSize' o CGRect como propiedad rect.size.height si está buscando la altura.

Más información sobre NSParagraphStyle aquí: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html


1
Parece que hay un problema de sintaxis inmediatamente después de ajustadoSize.
Chris Prince

Gracias por resolver ese problema de sintaxis, Chris
bitsand

6
// max size constraint
CGSize maximumLabelSize = CGSizeMake(184, FLT_MAX)

// font
UIFont *font = [UIFont fontWithName:TRADE_GOTHIC_REGULAR size:20.0f];

// set paragraph style
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

// dictionary of attributes
NSDictionary *attributes = @{NSFontAttributeName:font,
                             NSParagraphStyleAttributeName: paragraphStyle.copy};

CGRect textRect = [string boundingRectWithSize: maximumLabelSize
                                     options:NSStringDrawingUsesLineFragmentOrigin
                                  attributes:attributes
                                     context:nil];

CGSize expectedLabelSize = CGSizeMake(ceil(textRect.size.width), ceil(textRect.size.height));

1
Esto me ahorró horas de dolores de cabeza, gracias Kirit, definir los atributos y diccionarios antes del bloque CGRect fue lo que me ayudó ... también fue mucho más fácil de leer.
Azin Mehrnoosh

3

Cree una función que tome una instancia de UILabel. y devuelve CGSize

CGSize constraint = CGSizeMake(label.frame.size.width , 2000.0);
// Adjust according to requirement

CGSize size;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){

    NSRange range = NSMakeRange(0, [label.attributedText length]);

    NSDictionary *attributes = [label.attributedText attributesAtIndex:0 effectiveRange:&range];
    CGSize boundingBox = [label.text boundingRectWithSize:constraint options: NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;

    size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
}
else{
    size = [label.text sizeWithFont:label.font constrainedToSize:constraint lineBreakMode:label.lineBreakMode];
}

return size;

En mi caso para la altura de fila dinámicamente, eliminé por completo el método HeightForRow y usé UITableViewAutomaticDimension. tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Eugene Braginets

3

Solución alternativa

CGSize expectedLabelSize;
if ([subTitle respondsToSelector:@selector(sizeWithAttributes:)])
{
    expectedLabelSize = [subTitle sizeWithAttributes:@{NSFontAttributeName:subTitleLabel.font}];
}else{
    expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}

1
Esto producirá una advertencia del compilador en iOS6 y 7. ¡Y tendrá un comportamiento bastante diferente para cada uno!
Nick H247

3

Partiendo de @bitsand, este es un nuevo método que acabo de agregar a mi categoría NSString + Extras:

- (CGRect) boundingRectWithFont:(UIFont *) font constrainedToSize:(CGSize) constraintSize lineBreakMode:(NSLineBreakMode) lineBreakMode;
{
    // set paragraph style
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [style setLineBreakMode:lineBreakMode];

    // make dictionary of attributes with paragraph style
    NSDictionary *sizeAttributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: style};

    CGRect frame = [self boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:sizeAttributes context:nil];

    /*
    // OLD
    CGSize stringSize = [self sizeWithFont:font
                              constrainedToSize:constraintSize
                                  lineBreakMode:lineBreakMode];
    // OLD
    */

    return frame;
}

Solo uso el tamaño del cuadro resultante.


2

Aún puedes usar sizeWithFont. pero, en iOS> = 7.0, el método se bloquea si la cadena contiene espacios iniciales y finales o líneas finales \n.

Recortar texto antes de usarlo

label.text = [label.text stringByTrimmingCharactersInSet:
             [NSCharacterSet whitespaceAndNewlineCharacterSet]];

Eso también puede aplicarse a sizeWithAttributesy [label sizeToFit].

Además, siempre que tenga nsstringdrawingtextstorage message sent to deallocated instanceen un dispositivo iOS 7.0 se ocupa de esto.


2

Mejor uso de dimensiones automáticas (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

NB: 1. El prototipo UITableViewCell debe estar correctamente diseñado (por ejemplo, no olvide establecer UILabel.numberOfLines = 0, etc.) 2. Eliminar el método HeightForRowAtIndexPath

ingrese la descripción de la imagen aquí

VIDEO: https://youtu.be/Sz3XfCsSb6k


Es tomar la altura de la celda estática que se define en la celda prototipo del guión gráfico
Kirit Vaghela

Agregué esas dos líneas y me aseguré de que mi UILabel estuviera configurado en 0 número de líneas. No funcionó como se anuncia. ¿Qué más hiciste para que funcionara solo con esas dos líneas de código?
Will

1
hm, también tienes que fijar tus restricciones de etiquetas en la parte superior e inferior de la celda
Eugene Braginets

1
No, esto debería ser suficiente. ¿Tiene restricciones de distribución automática para la etiqueta anclada a la supervista?
Eugene Braginets

1
boundingRectWithSize:options:attributes:context:

1

La respuesta aceptada en Xamarin sería (use sizeWithAttributes y UITextAttributeFont):

        UIStringAttributes attributes = new UIStringAttributes
        { 
            Font = UIFont.SystemFontOfSize(17) 
        }; 
        var size = text.GetSizeUsingAttributes(attributes);

1

Como la respuesta de @Ayush:

Como puede ver sizeWithFonten el sitio para desarrolladores de Apple, está en desuso, por lo que debemos usarlo sizeWithAttributes.

Bueno, suponiendo que en 2019+ probablemente esté usando Swift y en Stringlugar de Objective-c y NSString, esta es la forma correcta de obtener el tamaño de una Stringfuente predefinida:

let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])

¿Cómo podrías usar esto para determinar el tamaño de fuente?
Joseph Astrahan

@JosephAstrahan esto no es para determinar el tamaño de la fuente, es para obtener el tamaño (CGSize) de una cadena con una fuente determinada. Si desea obtener el tamaño de fuente de una etiqueta, puede usar fácilmente label.font
Romulo BM

utilizando su idea, creé una forma de obtener el tamaño de fuente más o menos ( stackoverflow.com/questions/61651614/… ), label.font no funcionaría en mi caso debido a algunos problemas complicados con las etiquetas haciendo clic solo si el se sabe la fuente exacta, puedes leer sobre ella en mi publicación.
Joseph Astrahan

0
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

0

Aquí está el equivalente de monotouch si alguien lo necesita:

/// <summary>
/// Measures the height of the string for the given width.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="width">The width.</param>
/// <param name="padding">The padding.</param>
/// <returns></returns>
public static float MeasureStringHeightForWidth(this string text, UIFont font, float width, float padding = 20)
{
    NSAttributedString attributedString = new NSAttributedString(text, new UIStringAttributes() { Font = font });
    RectangleF rect = attributedString.GetBoundingRect(new SizeF(width, float.MaxValue), NSStringDrawingOptions.UsesLineFragmentOrigin, null);
    return rect.Height + padding;
}

que se puede usar así:

public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
    //Elements is a string array
    return Elements[indexPath.Row].MeasureStringHeightForWidth(UIFont.SystemFontOfSize(UIFont.LabelFontSize), tableView.Frame.Size.Width - 15 - 30 - 15);
}

0
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;

0

Prueba esta sintaxis:

NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];

-1

Nada de esto funcionó para mí en iOS 7. Esto es lo que terminé haciendo. Puse esto en mi clase de celda personalizada y llamo al método en mi método heightForCellAtIndexPath.

Mi celda se parece a la celda de descripción cuando veo una aplicación en la tienda de aplicaciones.

Primero en el guión gráfico, establezca su etiqueta en 'atributosTexto', establezca el número de líneas en 0 (lo que redimensionará la etiqueta automáticamente (solo ios 6+)) y configúrelo en ajuste de texto.

Luego solo agrego todas las alturas del contenido de la celda en mi Clase de celda personalizada. En mi caso, tengo una etiqueta en la parte superior que siempre dice "Descripción" (_descriptionHeadingLabel), una etiqueta más pequeña que es de tamaño variable que contiene la descripción real (_descriptionLabel) una restricción desde la parte superior de la celda hasta el encabezado (_descriptionHeadingLabelTopConstraint) . También agregué 3 para espaciar un poco la parte inferior (aproximadamente la misma cantidad que Apple coloca en la celda del tipo de subtítulo).

- (CGFloat)calculateHeight
{
    CGFloat width = _descriptionLabel.frame.size.width;
    NSAttributedString *attributedText = _descriptionLabel.attributedText;
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];

    return rect.size.height + _descriptionHeadingLabel.frame.size.height + _descriptionHeadingLabelTopConstraint.constant + 3;
}

Y en mi delegado de Vista de tabla:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    if (indexPath.row == 0) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"descriptionCell"];
        DescriptionCell *descriptionCell = (DescriptionCell *)cell;
        NSString *text = [_event objectForKey:@"description"];
        descriptionCell.descriptionLabel.text = text;

        return [descriptionCell calculateHeight];
    }

    return 44.0f;
}

Puede cambiar la instrucción if para que sea un poco más 'inteligente' y realmente obtenga el identificador de celda de algún tipo de fuente de datos. En mi caso, las celdas estarán codificadas, ya que habrá una cantidad fija de ellas en un orden específico.


boundingRectWithSizeen ios 9.2 problemas actuales, es resultados diferentes a ios <9.2. Encontraste o conoces cualquier otra mejor manera de hacer esto.
jose920405
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.