Sostituzione per dimensioni obsolete Con iTunes: in iOS 7?


320

In iOS 7, sizeWithFont:ora è obsoleto. Come posso ora passare l'oggetto UIFont nel metodo di sostituzione sizeWithAttributes:?

Risposte:


521

Usa sizeWithAttributes:invece, che ora richiede un NSDictionary. Passa la coppia con chiave UITextAttributeFonte il tuo oggetto font in questo modo:

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));

60
quando si lavora con un NSStringe un UILabel(non SEMPRE il caso, ma spesso così), per evitare duplicati di codice / ecc., è anche possibile sostituirlo [UIFont systemFontOfSize:17.0f]con label.font- aiuta la manutenzione del codice facendo riferimento a dati esistenti anziché digitandoli più volte o facendo riferimento a costanti in tutto il posto, ecc.
toblerpwn,

8
@toblerpwn è possibile che l'etichetta non esista e stai provando a calcolare un'etichetta teorica.
botbot,

5
Come userei questo esempio per ottenere size.height con un'etichetta che ha una larghezza fissa, ad esempio 250? O se è un'etichetta con strega autolatyout occupa un procentage della larghezza e vado in modalità paesaggistica.
Pedroinpeace,

12
@Pedroinpeace Dovresti boundingRectWithSize:options:attributes:context:invece usare , passando CGSizeMake(250.0f, CGFLOAT_MAX)nella maggior parte dei casi.
James Kuang,

9
Qualcos'altro da notare sizeWithAttributes: non è equivalente al 100%. sizeWithFont utilizzato per arrotondare le dimensioni a valori interi (pixel). Suggerire di usare ceilf sull'altezza / larghezza risultante o si potrebbero ottenere artefatti sfocati (esp non retina HW) se lo si utilizza per calcoli posizionali.
Nick H247,

172

Credo che la funzione sia stata deprecata perché quella serie di NSString+UIKitfunzioni ( sizewithFont:..., ecc.) Erano basate sulla UIStringDrawinglibreria, che non era thread-safe. Se hai provato a eseguirli non sul thread principale (come qualsiasi altra UIKitfunzionalità), otterrai comportamenti imprevedibili. In particolare, se hai eseguito la funzione su più thread contemporaneamente, probabilmente si bloccherà la tua app. Questo è il motivo per cui in iOS 6, hanno introdotto un boundingRectWithSize:...metodo per NSAttributedString. Questo è stato creato in cima alle NSStringDrawinglibrerie ed è thread-safe.

Se osservi la nuova NSString boundingRectWithSize:...funzione, richiede una matrice di attributi allo stesso modo di a NSAttributeString. Se dovessi indovinare, questa nuova NSStringfunzione in iOS 7 è semplicemente un wrapper per la NSAttributeStringfunzione di iOS 6.

In quella nota, se tu supportassi solo iOS 6 e iOS 7, cambierei sicuramente tutti i tuoi NSString sizeWithFont:...in NSAttributeString boundingRectWithSize. Ti farà risparmiare un sacco di mal di testa se ti capita di avere una strana custodia ad angolo multi-threading! Ecco come mi sono convertito NSString sizeWithFont:constrainedToSize::

Quello che era:

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

Può essere sostituito 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;

Si prega di notare che la documentazione menziona:

In iOS 7 e versioni successive, questo metodo restituisce dimensioni frazionarie (nel componente dimensioni del reso CGRect); per utilizzare una dimensione restituita per visualizzare le dimensioni, è necessario utilizzare aumentare il valore all'intero superiore più vicino utilizzando la funzione ceil.

Quindi, per estrarre l'altezza o la larghezza calcolata da utilizzare per il dimensionamento delle viste, utilizzerei:

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

boundingRectWithSize è obsoleto in iOS 6.0.
Nirav,

2
@Nirav Non vedo alcuna menzione della deprecazione. Puoi indicarmi dove dice che è deprecato? Grazie! ( developer.apple.com/library/ios/documentation/uikit/reference/… )
Mr. T

2
Forse @Nirav significava che non era disponibile per NSString in iOS 6 (che è menzionato indirettamente nella risposta)?
newenglander,

1
@VagueExplanation L'ho appena provato e boundingRectForSize non è ancora deprecato per NSAttributedString. Inoltre non dice deprecato nella documentazione.
Mr. T

5
Fantastico per menzionare l'utilizzo di ceilf (). In nessun altro luogo è stato detto e la mia dimensione era sempre leggermente troppo piccola. Grazie!
Jonathan Brown,

29

Come puoi vedere sizeWithFontsul sito degli sviluppatori Apple, è obsoleto, quindi è necessario utilizzarlo 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
In questo caso, meglio utilizzare [NSObject respondsToSelector:]il metodo come qui: stackoverflow.com/a/3863039/1226304
derpoliuk

16

Ho creato una categoria per gestire questo problema, eccolo qui:

#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]);
}

In questo modo si ha solo per trovare / sostituire sizeWithFont:con sizeWithMyFont:e si sta bene ad andare.


1
sebbene ciò produrrà un avviso del compilatore su ios7 + per fare riferimento a sizeWithFont o un avviso / errore di compilazione su <ios7 per fare riferimento a sizeWithAttributes! Probabilmente è meglio usare una macro invece di controllare respondsToSelector - se necessario. Ma dal momento che non puoi più consegnare a apple con ioS6 SDK ... probabilmente non lo è!
Nick H247,

Aggiungi questo per sopprimere l'avvertimento #pragma GCC diagnostico ignorato "-Wdeprecated-dichiarazioni"
Ryan Heitner

10

In iOS7 avevo bisogno della logica per restituire l'altezza corretta per il metodo tableview: heightForRowAtIndexPath, ma sizeWithAttributes restituisce sempre la stessa altezza indipendentemente dalla lunghezza della stringa perché non sa che verrà inserita in una cella di tabella a larghezza fissa . Ho trovato che funziona alla grande per me e calcola l'altezza corretta tenendo conto della larghezza della cella della tabella! Questo si basa sulla risposta di Mr. T sopra.

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

Le etichette multilinea che utilizzano l'altezza dinamica potrebbero richiedere ulteriori informazioni per impostare correttamente le dimensioni. È possibile utilizzare sizeWithAttributes con UIFont e NSParagraphStyle per specificare sia il carattere che la modalità di interruzione di riga.

Definiresti lo stile di paragrafo e utilizzeresti un NSDictionary come questo:

// 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];

Puoi usare CGSize 'adjustedSize' o CGRect come proprietà rect.size.height se stai cercando l'altezza.

Maggiori informazioni su NSParagraphStyle qui: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html


1
Sembra che ci sia un problema di sintassi subito dopo adjustedSize.
Chris Prince,

Grazie per aver
risolto il

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
Questo mi ha risparmiato ore di mal di testa, grazie Kirit, definire gli attributi e i dizionari prima del blocco CGRect è stato ciò che ha fatto per me ... anche molto più facile da leggere.
Azin Mehrnoosh,

3

Creare una funzione che accetta un'istanza di UILabel. e restituisce 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;

Il mio caso per l'altezza dinamica della riga ho rimosso completamente il metodo HeightForRow e usato UITableViewAutomaticDimension. tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Eugene Braginets,

3

Soluzione 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
Ciò produrrà un avviso del compilatore su iOS6 e 7. E avrà un comportamento abbastanza diverso per ciascuno!
Nick H247,

3

Basato su @bitsand, questo è un nuovo metodo che ho appena aggiunto alla mia categoria 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;
}

Uso solo le dimensioni del frame risultante.


2

Puoi ancora usare sizeWithFont. ma, in iOS> = 7.0 il metodo causa l'arresto anomalo se la stringa contiene spazi iniziali o finali o linee finali \n.

Ritagliare il testo prima di usarlo

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

Questo può anche valere per sizeWithAttributese[label sizeToFit] .

inoltre, ogni volta che hai nsstringdrawingtextstorage message sent to deallocated instancenel dispositivo iOS 7.0 si occupa di questo.


2

Meglio usare le dimensioni automatiche (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

NB: 1. Il prototipo UITableViewCell deve essere progettato correttamente (per l'istanza non dimenticare di impostare UILabel.numberOfLines = 0 ecc.) 2. Rimuovere il metodo HeightForRowAtIndexPath

inserisci qui la descrizione dell'immagine

VIDEO: https://youtu.be/Sz3XfCsSb6k


Ci vuole un'altezza statica della cella definita nella cellula prototipo dello storyboard
Kirit Vaghela,

Aggiunte quelle due linee e assicurato che il mio UILabel fosse impostato su 0 numero di linee. Non ha funzionato come pubblicizzato. Cos'altro hai fatto per farlo funzionare solo con quelle due righe di codice?
Will,

1
hm, anche tu devi fissare i tuoi vincoli di etichetta nella parte superiore e inferiore della cella
Eugene Braginets,

1
No, questo dovrebbe bastare. Hai vincoli di autolayout per l'etichetta bloccata su superview?
Eugene Braginets,

1
boundingRectWithSize:options:attributes:context:

1

La risposta accettata in Xamarin sarebbe (usa sizeWithAttributes e UITextAttributeFont):

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

1

Come la risposta di @Ayush:

Come puoi vedere sizeWithFontsul sito degli sviluppatori Apple, è obsoleto, quindi è necessario utilizzarlo sizeWithAttributes.

Bene, supponendo che nel 2019+ probabilmente stai usando Swift e Stringinvece di Objective-c e NSString, ecco il modo corretto di ottenere la dimensione di un Stringcarattere predefinito:

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

Come hai potuto utilizzare questo per determinare la dimensione del carattere?
Joseph Astrahan,

@JosephAstrahan questo non è per determinare la dimensione del carattere, ma per ottenere la dimensione (CGSize) di una stringa con carattere determinato. Se vuoi ottenere la dimensione del carattere di un'etichetta puoi usare facilmente label.font
Romulo BM

con la tua idea, ho creato un modo per ottenere la dimensione del carattere più o meno ( stackoverflow.com/questions/61651614/... ), label.font sarebbe non avrebbe funzionato nel mio caso a causa di alcuni problemi complessi con le etichette di essere cliccabile solo se il è noto il carattere esatto che puoi leggere a riguardo sul mio post.
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

Ecco l'equivalente monotouch se qualcuno ne ha bisogno:

/// <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;
}

che può essere usato in questo modo:

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

Prova questa sintassi:

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

-1

Niente di tutto ciò ha funzionato per me in iOS 7. Ecco cosa ho finito per fare. L'ho inserito nella mia classe di cella personalizzata e chiamo il metodo nel mio metodo heightForCellAtIndexPath.

La mia cella è simile alla cella della descrizione quando si visualizza un'app nell'app store.

Per prima cosa nello storyboard, imposta l'etichetta su 'AttribText', imposta il numero di righe su 0 (che ridimensionerà automaticamente l'etichetta (solo iOS 6+)) e impostalo sul ritorno a capo automatico.

Quindi aggiungo solo tutte le altezze del contenuto della cella nella mia classe cellulare personalizzata. Nel mio caso ho un'etichetta nella parte superiore che dice sempre "Descrizione" (_descriptionHeadingLabel), un'etichetta più piccola di dimensioni variabili che contiene la descrizione effettiva (_descriptionLabel) un vincolo dalla parte superiore della cella all'intestazione (_descriptionHeadingLabelTopConstraint) . Ho anche aggiunto 3 per distanziare un po 'la parte inferiore (circa la stessa quantità di posti di mela nella cella del tipo di sottotitolo).

- (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;
}

E nel mio delegato di Table View:

- (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;
}

È possibile modificare l'istruzione if in modo che sia un po 'più "intelligente" e in realtà ottenere l'identificatore di cella da una sorta di origine dati. Nel mio caso, le celle saranno codificate in modo rigido poiché ci sarà una quantità fissa di esse in un ordine specifico.


boundingRectWithSizein iOS 9.2 problemi attuali, sono risultati diversi da iOS <9.2. Hai trovato o conosci qualsiasi altro modo migliore per farlo.
jose920405,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.