Ersatz für veraltete GrößeWithFont: in iOS 7?


320

In iOS 7 sizeWithFont:ist jetzt veraltet. Wie übergebe ich jetzt das UIFont-Objekt an die Ersetzungsmethode sizeWithAttributes:?

Antworten:


521

Verwenden Sie sizeWithAttributes:stattdessen, was jetzt eine dauert NSDictionary. Übergeben Sie das Paar mit dem Schlüssel UITextAttributeFontund Ihrem Schriftobjekt wie folgt:

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
wenn sie mit einer Arbeit NSStringund ein UILabel(nicht immer der Fall, aber oft so), duplizierten Code zu verhindern / etc, können Sie auch ersetzen [UIFont systemFontOfSize:17.0f]mit label.font- hilft Code Wartung durch Verweisen auf vorhandene Daten vs. Sie es mehrmals oder Referenzierung Konstanten alle über die Eingabe Ort, etc
toblerpwn

8
@toblerpwn Es ist möglich, dass das Label nicht existiert und Sie versuchen, ein theoretisches Label zu berechnen.
Botbot

5
Wie würde ich dieses Beispiel verwenden, um die size.height mit einem Etikett zu erhalten, das eine feste Breite von beispielsweise 250 hat? ODER wenn es ein Etikett mit Autolatyout ist, nimmt die Hexe einen Prozentsatz der Breite ein und ich gehe in den Landschaftsmodus.
Pedroinpeace

12
@Pedroinpeace Sie würden boundingRectWithSize:options:attributes:context:stattdessen verwenden und CGSizeMake(250.0f, CGFLOAT_MAX)in den meisten Fällen übergeben.
James Kuang

9
Noch etwas zu beachten sizeWithAttributes: ist nicht 100% äquivalent. sizeWithFont wird verwendet, um die Größen auf ganzzahlige (Pixel) Werte aufzurunden. Schlagen Sie vor, Ceilf für die resultierende Höhe / Breite zu verwenden, da sonst unscharfe Artefakte (insbesondere HW) auftreten können, wenn Sie diese für Positionsberechnungen verwenden.
Nick H247

172

Ich glaube, die Funktion war veraltet, weil diese Reihe von NSString+UIKitFunktionen ( sizewithFont:...usw.) auf der UIStringDrawingBibliothek basierte , die nicht threadsicher war. Wenn Sie versucht haben, sie nicht im Hauptthread auszuführen (wie bei allen anderen UIKitFunktionen), treten unvorhersehbare Verhaltensweisen auf. Insbesondere wenn Sie die Funktion auf mehreren Threads gleichzeitig ausgeführt haben, wird Ihre App wahrscheinlich abstürzen. Aus diesem Grund haben sie in iOS 6 die boundingRectWithSize:...Methode für eingeführt NSAttributedString. Dies wurde auf den NSStringDrawingBibliotheken aufgebaut und ist threadsicher.

Wenn Sie sich die neue NSString boundingRectWithSize:...Funktion ansehen , werden Sie auf die gleiche Weise wie bei a nach einem Attributarray gefragt NSAttributeString. Wenn ich raten müsste, ist diese neue NSStringFunktion in iOS 7 lediglich ein Wrapper für die NSAttributeStringFunktion von iOS 6.

In diesem Sinne, wenn Sie nur iOS 6 und iOS 7 unterstützen würden, würde ich definitiv alle Ihre NSString sizeWithFont:...auf ändern NSAttributeString boundingRectWithSize. Es erspart Ihnen viel Kopfschmerzen, wenn Sie einen seltsamen Multithreading-Eckfall haben! So habe ich konvertiert NSString sizeWithFont:constrainedToSize::

Was früher war:

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

Kann ersetzt werden durch:

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;

Bitte beachten Sie die Dokumentation Erwähnungen:

In iOS 7 und höher gibt diese Methode gebrochene Größen zurück (in der Größenkomponente der zurückgegebenen CGRect). Um eine zurückgegebene Ansicht von Größe zu Größe zu verwenden, müssen Sie den Wert mithilfe der Ceil-Funktion auf die nächsthöhere Ganzzahl erhöhen.

Um die berechnete Höhe oder Breite herauszuziehen, die für die Größenanpassung von Ansichten verwendet werden soll, würde ich Folgendes verwenden:

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

boundingRectWithSize ist in iOS 6.0 veraltet.
Nirav

2
@Nirav Ich sehe keine Erwähnung der Abwertung. Können Sie mich darauf hinweisen, wo es heißt, dass es veraltet ist? Vielen Dank! ( developer.apple.com/library/ios/documentation/uikit/reference/… )
Mr. T

2
Vielleicht bedeutete @Nirav, dass es für NSString in iOS 6 nicht verfügbar war (was indirekt in der Antwort erwähnt wird)?
Newenglander

1
@VagueExplanation Ich habe das gerade versucht und boundingRectForSize ist für NSAttributedString immer noch nicht veraltet. In der Dokumentation heißt es auch nicht veraltet.
Herr T

5
Fantastisch für die Erwähnung mit Ceilf (). Nirgendwo sonst wurde das erwähnt und meine Größe war immer etwas zu klein. Vielen Dank!
Jonathan Brown

29

Wie Sie sizeWithFontauf der Apple Developer-Website sehen können, ist es veraltet, daher müssen wir es verwenden 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 diesem Fall ist es besser, die [NSObject respondsToSelector:]Methode wie hier zu verwenden: stackoverflow.com/a/3863039/1226304
derpoliuk

16

Ich habe eine Kategorie erstellt, um dieses Problem zu lösen. Hier ist sie:

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

Auf diese Weise können nur finden , haben / Ersetzen sizeWithFont:mit sizeWithMyFont:und du bist gut zu gehen.


1
Dies erzeugt jedoch eine Compiler-Warnung auf ios7 + zum Verweisen auf sizeWithFont oder eine Kompilierungswarnung / -fehler auf <ios7 zum Verweisen auf sizeWithAttributes! Verwenden Sie am besten ein Makro, anstatt respondsToSelector zu überprüfen - falls erforderlich. Aber da Sie mit ioS6 SDK nicht mehr an Apple liefern können, ist dies wahrscheinlich nicht der Fall!
Nick H247

Fügen Sie diese auf supress Warnung von Pragma GCC Diagnose ignoriert „-Wdeprecated-Erklärungen“
Ryan Heitner

10

In iOS7 brauchte ich die Logik, um die richtige Höhe für die Tabellenansicht zurückzugeben: heightForRowAtIndexPath-Methode, aber die sizeWithAttributes geben unabhängig von der Zeichenfolgenlänge immer dieselbe Höhe zurück, da sie nicht wissen, dass sie in eine Tabellenzelle mit fester Breite eingefügt werden . Ich fand das funktioniert gut für mich und berechnet die richtige Höhe unter Berücksichtigung der Breite für die Tabellenzelle! Dies basiert auf der obigen Antwort von Herrn 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

Für mehrzeilige Beschriftungen mit dynamischer Höhe sind möglicherweise zusätzliche Informationen erforderlich, um die Größe richtig einzustellen. Sie können sizeWithAttributes mit UIFont und NSParagraphStyle verwenden, um sowohl die Schriftart als auch den Zeilenumbruchmodus anzugeben.

Sie würden den Absatzstil definieren und ein NSDictionary wie folgt verwenden:

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

Sie können CGSize 'adjustedSize' oder CGRect als Eigenschaft rect.size.height verwenden, wenn Sie nach der Höhe suchen.

Weitere Informationen zu NSParagraphStyle finden Sie hier: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html


1
Es scheint, als gäbe es unmittelbar nach adjustSize ein Syntaxproblem.
Chris Prince

Vielen Dank, dass Sie dieses Syntaxproblem
behoben haben

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
Dies ersparte mir stundenlange Kopfschmerzen, danke Kirit. Die Definition der Attribute und Wörterbücher vor dem CGRect-Block hat mir geholfen ... auch viel einfacher zu lesen.
Azin Mehrnoosh

3

Erstellen Sie eine Funktion, die eine UILabel-Instanz akzeptiert. und gibt CGSize zurück

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;

In meinem Fall für dynamische Zeilenhöhe habe ich die HeightForRow-Methode vollständig entfernt und UITableViewAutomaticDimension verwendet. tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Eugene Braginets

3

Alternative Lösung

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
Dies wird sowohl auf iOS6 als auch auf iOS 7 eine Compiler-Warnung erzeugen.
Nick H247

3

Aufbauend auf @bitsand ist dies eine neue Methode, die ich gerade zu meiner Kategorie NSString + Extras hinzugefügt habe:

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

Ich benutze nur die Größe des resultierenden Rahmens.


2

Sie können immer noch verwenden sizeWithFont. In iOS> = 7.0 führt die Methode jedoch zum Absturz, wenn die Zeichenfolge führende und nachfolgende Leerzeichen oder Endzeilen enthält \n.

Beschneiden von Text vor der Verwendung

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

Das kann auch für sizeWithAttributesund gelten [label sizeToFit].

Wenn Sie ein nsstringdrawingtextstorage message sent to deallocated instanceiOS 7.0-Gerät verwenden, wird dies ebenfalls behandelt.


2

Verwenden Sie besser automatische Abmessungen (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

NB: 1. Der UITableViewCell-Prototyp sollte ordnungsgemäß entworfen sein (für die Instanz nicht vergessen, UILabel.numberOfLines = 0 usw. zu setzen). 2. Entfernen Sie die HeightForRowAtIndexPath-Methode

Geben Sie hier die Bildbeschreibung ein

VIDEO: https://youtu.be/Sz3XfCsSb6k


Es ist statische
Zellenhöhe

Fügte diese beiden Zeilen hinzu und stellte sicher, dass mein UILabel auf 0 Zeilen eingestellt war. Hat nicht wie angekündigt funktioniert. Was haben Sie noch getan, damit es nur mit diesen beiden Codezeilen funktioniert?
Will

1
hm, außerdem müssen Sie Ihre Beschriftungsbeschränkungen oben und unten in der Zelle
anheften

1
Nein, das sollte reichen. Haben Sie Autolayout-Einschränkungen für Etiketten, die an die Übersicht angeheftet sind?
Eugene Braginets


1

Akzeptierte Antwort in Xamarin wäre (benutze sizeWithAttributes und UITextAttributeFont):

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

1

Als @ Ayush-Antwort:

Wie Sie sizeWithFontauf der Apple Developer-Website sehen können, ist es veraltet, daher müssen wir es verwenden sizeWithAttributes.

Angenommen, Sie verwenden ab 2019 wahrscheinlich Swift und Stringanstelle von Objective-c und erhalten NSStringauf die richtige Weise die Größe einer Stringmit vordefinierter Schriftart:

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

Wie können Sie dies verwenden, um die Schriftgröße zu bestimmen?
Joseph Astrahan

@JosephAstrahan Dies dient nicht zum Bestimmen der Schriftgröße, sondern zum Abrufen der Größe (CGSize) einer Zeichenfolge mit bestimmter Schriftart. Wenn Sie die Schriftgröße eines Etiketts erhalten möchten, können Sie label.font
Romulo BM

Mit Ihrer Idee habe ich eine Möglichkeit geschaffen, die Schriftgröße mehr oder weniger zu ermitteln ( stackoverflow.com/questions/61651614/… ). label.font würde in meinem Fall aufgrund einiger komplizierter Probleme mit anklickbaren Beschriftungen nur dann nicht funktionieren, wenn die Die genaue Schriftart ist bekannt. Sie können sie in meinem Beitrag lesen.
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

Hier ist das Monotouch-Äquivalent, wenn jemand es braucht:

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

welches so verwendet werden kann:

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

Versuchen Sie diese Syntax:

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

-1

Nichts davon hat in iOS 7 für mich funktioniert. Hier ist, was ich letztendlich getan habe. Ich füge dies in meine benutzerdefinierte Zellenklasse ein und rufe die Methode in meiner heightForCellAtIndexPath-Methode auf.

Meine Zelle ähnelt der Beschreibungszelle beim Anzeigen einer App im App Store.

Setzen Sie zuerst im Storyboard Ihre Beschriftung auf "attributedText", setzen Sie die Anzahl der Zeilen auf 0 (wodurch die Größe der Beschriftung automatisch geändert wird (nur ios 6+)) und setzen Sie sie auf Zeilenumbruch.

Dann addiere ich einfach alle Höhen des Inhalts der Zelle in meiner benutzerdefinierten Zellenklasse. In meinem Fall habe ich oben eine Beschriftung mit der Aufschrift "Description" (_descriptionHeadingLabel), eine kleinere Beschriftung mit variabler Größe, die die tatsächliche Beschreibung (_descriptionLabel) enthält, eine Einschränkung vom oberen Rand der Zelle bis zur Überschrift (_descriptionHeadingLabelTopConstraint). . Ich habe auch 3 hinzugefügt, um den Boden ein wenig auszuräumen (ungefähr die gleiche Menge Apfel platziert auf der Zelle vom Untertiteltyp.)

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

Und in meinem Delegierten für Tabellenansicht:

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

Sie können die if-Anweisung so ändern, dass sie etwas intelligenter ist, und die Zellenkennung tatsächlich aus einer Datenquelle abrufen. In meinem Fall werden die Zellen hartcodiert, da es eine feste Menge von ihnen in einer bestimmten Reihenfolge gibt.


boundingRectWithSizein ios 9.2 Probleme darstellen, gibt es andere Ergebnisse als in ios <9.2. Sie haben einen anderen besten Weg gefunden oder kennen ihn, um dies zu erreichen.
Jose920405
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.