boundingRectWithSize für NSAttributedString gibt eine falsche Größe zurück

151

Ich versuche, das Rect für eine zugewiesene Zeichenfolge abzurufen, aber der Aufruf von boundingRectWithSize berücksichtigt nicht die Größe, die ich übergebe, und gibt ein Rect mit einer einzelnen Zeilenhöhe im Gegensatz zu einer großen Höhe zurück (es ist eine lange Zeichenfolge). Ich habe experimentiert, indem ich einen sehr großen Wert für die Höhe und auch 0 wie im folgenden Code übergeben habe, aber das zurückgegebene Rect ist immer das gleiche.

CGRect paragraphRect = [attributedText boundingRectWithSize:CGSizeMake(300,0.0)
  options:NSStringDrawingUsesDeviceMetrics
  context:nil];

Ist das kaputt oder muss ich etwas anderes tun, damit es einen Fehler für umbrochenen Text zurückgibt?

RunLoop
quelle
5
Haben Sie zufällig einen Absatzstil mit Abschneiden / Ausschneiden lineBreakMode?
Danyowdee
1
Wenn Sie dies lesen, weil UILabel auf eine falsche Breite misst / einwickelt, werfen Sie einen Blick auf stackoverflow.com/questions/46200027/… . Insbesondere wird NSAllowsDefaultLineBreakStrategy beim Start der App auf false gesetzt.
Eric

Antworten:

312

Sieht so aus, als hätten Sie nicht die richtigen Optionen angegeben. Geben Sie zum Verpacken von Etiketten mindestens Folgendes an:

CGRect paragraphRect =
  [attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
  options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
  context:nil];

Hinweis: Wenn die ursprüngliche Textbreite unter 300 liegt. Wenn kein Zeilenumbruch erfolgt, stellen Sie sicher, dass die gebundene Größe korrekt ist. Andernfalls erhalten Sie immer noch falsche Ergebnisse.

Ed McManus
quelle
25
Jeder muss sich diese Antwort ansehen, weil sie funktioniert. Möglicherweise ist für diese Methode eine klarere Dokumentation erforderlich, und es werden im Allgemeinen Zeichenfolgen zugewiesen. Es ist nicht klar, wo Sie suchen müssen.
Macserv
31
Beachtung! Es funktioniert nicht immer! Manchmal ist die zurückgegebene Breite größer als die Breite des Größenparameters. In der Dokumentation zur Apples-Methode heißt es: "Die Einschränkungen, die Sie im Parameter size angeben, sind eine Anleitung für den Renderer zur Größenanpassung der Zeichenfolge. Das von dieser Methode zurückgegebene tatsächliche Begrenzungsrechteck kann jedoch größer sein als die Einschränkungen, wenn zusätzlicher Speicherplatz erforderlich ist Rendern Sie die gesamte Zeichenfolge. "
Klaas
75
Die API für NSAttributedString und boundingRectWithSize ist absolut schockierend.
Malhal
27
Ein weiteres Problem ist, dass die im AbsatzRect zurückgegebene Höhe (und vermutlich die Breite) fast immer ein Bruchwert ist. So kann es herauskommen, 141,3 als die Höhe zu sagen. Sie müssen das Ergebnis von verwenden, ceilf(paragraphRect.size.height)damit es aufgerundet wird. Ich vergesse das die ganze Zeit und frage mich, warum meine Etiketten immer noch beschneiden.
Jamone
39
Ich verpacke meine boundingRectWithSize:Berechnungen immer in CGRectIntegral (), wodurch CGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integersin diesem Fall Höhe und Breite aufgerundet werden , um sicherzustellen, dass kein Beschneiden auftritt, wenn Höhe oder Breite ein Bruchwert sind.
Runmad
47

Aus irgendeinem Grund gibt boundingRectWithSize immer eine falsche Größe zurück. Ich habe eine Lösung gefunden. Für UItextView -sizeThatFits gibt es eine Methode, die die richtige Größe für den Textsatz zurückgibt. Erstellen Sie also anstelle von boundingRectWithSize eine UITextView mit einem zufälligen Frame und rufen Sie deren sizeThatFits mit der jeweiligen Breite und CGFLOAT_MAX-Höhe auf. Es wird die Größe zurückgegeben, die die richtige Höhe hat.

   UITextView *view=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 10)];   
   view.text=text;
   CGSize size=[view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
   height=size.height; 

Wenn Sie die Größe in einer while-Schleife berechnen, vergessen Sie nicht, hinzuzufügen, dass in einem Autorelease-Pool, da n UITextView erstellt wird, der Laufzeitspeicher der App erhöht wird, wenn wir keinen Autoreleasepool verwenden.

shoan
quelle
1
Das funktioniert. Die anderen Wege, die ich versuchte, ich konnte nie zur Arbeit kommen. Ich denke, mein Problem ergibt sich aus der Komplexität der zugewiesenen Zeichenfolge, die ich zugewiesen habe. Es stammte aus einer RTF-Datei und verwendete eine Vielzahl von Schriftarten, Zeilenabständen und sogar Schatten. Trotz der Verwendung von UsesLineFragmentOrigin und UsesFontLeading konnte ich nie ein ausreichend großes Ergebnis erzielen, und das Problem war umso schlimmer, je länger die zugewiesene Zeichenfolge wurde. Meine Vermutung ist , dass für relativ einfache Strings der boundingRectWithSize Methode kann arbeiten. Sie brauchen wirklich eine bessere Methode, um damit zu arbeiten, imo.
John Bushnell
Denken Sie nicht, dass es übertrieben ist, UITextViewso viele Zeit zu erstellen, wie die heightForRowAtIndexPathMethode aufgerufen wird? es braucht Zeit
János
Ich stimme Ihnen bei János zu, aber dies war die einzige Lösung, die für mich funktioniert hat.
Shoan
Übrigens habe ich nach der richtigen Lösung gefragt: stackoverflow.com/questions/32495744/…
János
Dies ist die einzige Lösung, die funktioniert. Dieses Problem hat 9 Jahre und Apple will es anscheinend nicht lösen oder ihre Ingenieure sind zu schwach, um eine Lösung dafür zu finden. Wir sprechen hier über iOS 11, das immer noch den gleichen Fehler aufweist.
Ente
31

Ed McManus hat sicherlich einen Schlüssel geliefert, um dies zum Laufen zu bringen. Ich habe einen Fall gefunden, der nicht funktioniert

UIFont *font = ...
UIColor *color = ...
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                     font, NSFontAttributeName,
                                     color, NSForegroundColorAttributeName,
                                     nil];

NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: someString attributes:attributesDictionary];

[string appendAttributedString: [[NSAttributedString alloc] initWithString: anotherString];

CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

rect hat nicht die richtige Höhe. Beachten Sie, dass ein anderer String (der an eine Zeichenfolge angehängt wird ) ohne ein Attributwörterbuch initialisiert wurde. Dies ist ein legitimer Initialisierer für einen anderen String, aber boundingRectWithSize: gibt in diesem Fall keine genaue Größe an.

Paul Heller
quelle
33
Danke dir! Es stellt sich heraus, dass für JEDEN Teil eines NSAttributedString ein Wörterbuch mit mindestens NSFontAttributeName und NSForegroundColorAttributeName festgelegt sein muss, wenn Sie möchten, dass boundingRectWithSize tatsächlich funktioniert! Ich sehe das nirgendwo dokumentiert.
Ben Wheeler
3
Beachten Sie auch, dass die verschiedenen NSAttributedStrings, die zu einem einzigen kombiniert werden, alle das gleiche Wörterbuch (und damit dieselbe Schriftart) verwenden müssen, damit boundingRectWithSize ordnungsgemäß funktioniert!
Ben Wheeler
1
Diese Lösung ist der für meine UILabel-Kategorie für "Shrink to Fit" sehr ähnlich. Der Schlüssel für mich war, das Begrenzungsrechteck mit der Höhe FLT_MAX zu erstellen. Ohne das würde ich ein Rechteck für nur eine Zeile bekommen.
dre1138
+ 1s rundum. Das hat mich wirklich erwischt, also danke, dass du es ausgearbeitet hast, Leute.
Wex
Ich hatte dieses Problem. Ich habe Leerzeichen und Zeilenumbrüche NSMutableAttributedStringohne Attribute verwendet und die falsche Größe erhalten.
Vbezhenar
28

Meine endgültige Entscheidung nach langer Untersuchung: Die
- boundingRectWithSizeFunktion gibt die richtige Größe nur für eine ununterbrochene Zeichenfolge zurück! Falls die Zeichenfolge Leerzeichen oder etwas anderes enthält (von Apple "Einige der Glyphen" genannt) - ist es unmöglich, die tatsächliche Größe des für die Anzeige von Text erforderlichen Rect zu ermitteln!
Ich habe Leerzeichen in meinen Zeichenfolgen durch Buchstaben ersetzt und sofort das richtige Ergebnis erhalten.

Apple sagt hier: https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

"Diese Methode gibt die tatsächlichen Grenzen der Glyphen in der Zeichenfolge zurück. Einige der Glyphen (z. B. Leerzeichen) dürfen die Layoutbeschränkungen überlappen, die durch die übergebene Größe angegeben werden, in einigen Fällen auch den Breitenwert der Größenkomponente von Der zurückgegebene CGRectWert kann den Breitenwert des Größenparameters überschreiten. "

Es ist also notwendig, einen anderen Weg zu finden, um die tatsächliche ...


Nach langem Untersuchungsprozess endlich Lösung gefunden !!! Ich bin nicht sicher, ob es in allen Fällen gut funktioniert UITextView, aber die wichtigste und wichtigste Sache wurde entdeckt!

boundingRectWithSize Funktion sowie CTFramesetterSuggestFrameSizeWithConstraints (und viele andere Methoden) berechnen Größe und Textanteil korrekt, wenn das richtige Rechteck verwendet wird. Zum Beispiel - UITextViewhat textView.bounds.size.width- und dieser Wert ist kein tatsächliches Rechteck, das vom System beim Zeichnen von Text verwendet wird UITextView.

Ich fand sehr interessante Parameter und führte eine einfache Berechnung im Code durch:

CGFloat padding = textView.textContainer.lineFragmentPadding;  
CGFloat  actualPageWidth = textView.bounds.size.width - padding * 2;

Und Magie funktioniert - alle meine Texte sind jetzt korrekt berechnet! Genießen!

Юрий Сталоверов
quelle
1
Ja, ich habe auch bemerkt, dass es die Einschränkungen für die Breite nicht beibehält, es kommt immer größer heraus. Das ist wirklich nicht cool, sie haben die Arbeitsweise veraltet und jetzt müssen wir uns mit diesem Mist befassen.
Boris Gafurov
1
Sie, mein Herr, sind mein neuer Held! Das hat mich verrückt gemacht. Ich setze die textContainer-Einfügungen und musste daher textView.textContainer.size.width anstelle von textView.bounds.size.witdh verwenden.
GCBenson
Ich konnte das nicht genug abstimmen. So seltsam, dass Leerzeichen bei Grenzberechnungen nicht berücksichtigt werden.
Chase Holland
1
Das Ersetzen der Leerzeichen durch ein Dummy-Zeichen wie "3" gibt die genaue Höhe zurück
Abuzar Amin
1
Dies rettete meine Karriere und meine Beziehung zum QA-Team. Ich schulde dir einen Biermann !!
Lucaslt89
14

Schnelle vier Version

let string = "A great test string."
let font = UIFont.systemFont(ofSize: 14)
let attributes: [NSAttributedStringKey: Any] = [.font: font]
let attributedString = NSAttributedString(string: string, attributes: attributes)
let largestSize = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)

//Option one (best option)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
let textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRange(), nil, largestSize, nil)

//Option two
let textSize = (string as NSString).boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], attributes: attributes, context: nil).size

//Option three
let textSize = attributedString.boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], context: nil).size

Das Messen des Texts mit dem CTFramesetter funktioniert am besten, da er ganzzahlige Größen bietet und Emojis und andere Unicode-Zeichen gut verarbeitet.

David Rees
quelle
10

Ich hatte mit keinem dieser Vorschläge Glück. Meine Zeichenfolge enthielt Unicode-Aufzählungszeichen, und ich vermute, dass sie bei der Berechnung Kummer verursachten. Ich bemerkte, dass UITextView die Zeichnung gut handhabte, also habe ich darauf geachtet, um die Berechnung zu nutzen. Ich habe Folgendes getan, was wahrscheinlich nicht so optimal ist wie die NSString-Zeichenmethoden, aber zumindest genau. Es ist auch etwas optimaler als das Initialisieren einer UITextView, um sie nur aufzurufen -sizeThatFits:.

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(width, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:formattedString];
[textStorage addLayoutManager:layoutManager];

const CGFloat formattedStringHeight = ceilf([layoutManager usedRectForTextContainer:textContainer].size.height);
Ricky
quelle
Möglicherweise waren es nicht die Unicode-Punkte, sondern Leerzeichen am Ende Ihrer Zeichenfolge. Siehe die Antwort oben: stackoverflow.com/a/25941139/337934 .
SamB
9

Falls Sie einen Begrenzungsrahmen erhalten möchten, indem Sie den Schwanz abschneiden, kann Ihnen diese Frage helfen.

CGFloat maxTitleWidth = 200;

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

NSDictionary *attributes = @{NSFontAttributeName : self.textLabel.font,
                             NSParagraphStyleAttributeName: paragraph};

CGRect box = [self.textLabel.text
              boundingRectWithSize:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)
              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
              attributes:attributes context:nil];
Roman B.
quelle
9

Es stellt sich heraus, dass für JEDEN Teil eines NSAttributedString ein Wörterbuch mit mindestens NSFontAttributeName und NSForegroundColorAttributeName festgelegt sein muss, wenn Sie möchten, dass boundingRectWithSize tatsächlich funktioniert!

Ich sehe das nirgendwo dokumentiert.

Ben Wheeler
quelle
Vielen Dank, dies war die Lösung für mich, außer dass ich festgestellt habe, dass nur NSFontAttributeName erforderlich ist, nicht NSForegroundColorAttributeName
Marmoy
7

@ Warrenm Tut mir leid zu sagen, dass die Framesetter-Methode bei mir nicht funktioniert hat.

Ich habe dies erhalten. Diese Funktion kann uns helfen, die Frame-Größe zu bestimmen, die für einen Zeichenfolgenbereich eines NSAttributedString im iPhone / Ipad SDK für eine bestimmte Breite benötigt wird:

Es kann für eine dynamische Höhe von UITableView-Zellen verwendet werden

- (CGSize)frameSizeForAttributedString:(NSAttributedString *)attributedString
{
    CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CGFloat width = YOUR_FIXED_WIDTH;

    CFIndex offset = 0, length;
    CGFloat y = 0;
    do {
        length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
        CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));

        CGFloat ascent, descent, leading;
        CTLineGetTypographicBounds(line, &ascent, &descent, &leading);

        CFRelease(line);

        offset += length;
        y += ascent + descent + leading;
    } while (offset < [attributedString length]);

    CFRelease(typesetter);

    return CGSizeMake(width, ceil(y));
}

Dank HADDAD ISSA >>> http://haddadissa.blogspot.in/2010/09/compute-needed-heigh-for-fixed-width-of.html

Karan Alangat
quelle
Funktioniert bei mir auf keinem Gerät (iPhone 4 und 5). Sowohl auf dem Gerät mit iOS7.1.2 als auch auf dem 4s Simulator mit iOS8.3 :(
Sanjana
Benötigen Sie einen Import?
Jose920405
@ KaranAlangat ja#import <CoreText/CoreText.h>
Jose920405
7

Ich habe festgestellt, dass die bevorzugte Lösung keine Zeilenumbrüche behandelt.

Ich habe festgestellt, dass dieser Ansatz in allen Fällen funktioniert:

UILabel* dummyLabel = [UILabel new];
[dummyLabel setFrame:CGRectMake(0, 0, desiredWidth, CGFLOAT_MAX)];
dummyLabel.numberOfLines = 0;
[dummyLabel setLineBreakMode:NSLineBreakByWordWrapping];
dummyLabel.attributedText = myString;
[dummyLabel sizeToFit];
CGSize requiredSize = dummyLabel.frame.size;
dmc
quelle
In meinem Fall sollte ich die Berechnung für den Hintergrund-Thread durchführen, und die Verwendung von UI-Elementen ist nicht zulässig
Lubbo
4

Ich hatte das gleiche Problem damit, mit diesen Techniken keine genaue Größe zu erhalten, und ich habe meinen Ansatz geändert, damit es funktioniert.

Ich habe eine lange zugeschriebene Zeichenfolge, die ich versucht habe, in eine Bildlaufansicht einzufügen, damit sie richtig angezeigt wird, ohne abgeschnitten zu werden. Was ich getan habe, damit der Text zuverlässig funktioniert, war, die Höhe überhaupt nicht als Einschränkung festzulegen und stattdessen die intrinsische Größe übernehmen zu lassen. Jetzt wird der Text korrekt angezeigt, ohne abgeschnitten zu werden, und ich muss die Höhe nicht berechnen.

Ich nehme an, wenn ich die Höhe zuverlässig ermitteln müsste, würde ich eine Ansicht erstellen, die ausgeblendet ist, und diese Einschränkungen und die Höhe des Rahmens ermitteln, sobald die Einschränkungen angewendet werden.

Brennan
quelle
2
Können Sie ein Codebeispiel hinzufügen, um dies zu demonstrieren? Vielen Dank.
Nathan Buggia
3

Ich bin etwas spät dran - aber ich habe versucht, einen Weg zu finden, wie ich den Begrenzungsrahmen finden kann, der um eine zugeordnete Zeichenfolge passt, um einen Fokusring zu erzeugen, wie es beim Bearbeiten einer Datei im Finder der Fall ist. Alles, was ich versucht hatte, schlug fehl, wenn am Ende der Zeichenfolge Leerzeichen oder mehrere Leerzeichen in der Zeichenfolge stehen. boundingRectWithSizescheitert kläglich daran CTFramesetterCreateWithAttributedString.

Die Verwendung NSLayoutManagerdes folgenden Codes scheint in allen bisher gefundenen Fällen den Trick zu tun und gibt ein Rect zurück, das die Zeichenfolge perfekt begrenzt. Bonus: Wenn Sie den Text auswählen, gehen die Kanten der Auswahl bis an die Grenzen des zurückgegebenen Rect. Der folgende Code verwendet den layoutManager von a NSTextView.

NSLayoutManager* layout = [self layoutManager];
NSTextContainer* container = [self textContainer];

CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container];
Chris Walken
quelle
2
textView.textContainerInset = UIEdgeInsetsZero;
NSString *string = @"Some string";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName:[UIColor blackColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[textView setAttributedText:attributedString];
CGRect textViewFrame = [textView.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.frame)-8.0f, 9999.0f) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
NSLog(@"%f", ceilf(textViewFrame.size.height));

Funktioniert auf allen Schriftarten perfekt!

Dmitry Coolerov
quelle
1

Ich hatte das gleiche Problem, aber ich erkannte, dass die Höhenbeschränkung korrekt eingestellt wurde. Also habe ich folgendes gemacht:

-(CGSize)MaxHeighForTextInRow:(NSString *)RowText width:(float)UITextviewWidth {

    CGSize constrainedSize = CGSizeMake(UITextviewWidth, CGFLOAT_MAX);

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [UIFont fontWithName:@"HelveticaNeue" size:11.0], NSFontAttributeName,
                                          nil];

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:RowText attributes:attributesDictionary];

    CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    if (requiredHeight.size.width > UITextviewWidth) {
        requiredHeight = CGRectMake(0, 0, UITextviewWidth, requiredHeight.size.height);
    }

    return requiredHeight.size;
}
Marcel
quelle
1
    NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [UIFont systemFontOfSize:18], NSFontAttributeName,
                                      [UIColor blackColor], NSForegroundColorAttributeName,
                                      nil];

    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myLabel.text attributes:stringAttributes];
    myLabel.attributedText = attributedString; //this is the key!

    CGSize maximumLabelSize = CGSizeMake (screenRect.size.width - 40, CGFLOAT_MAX);

    CGRect newRect = [myLabel.text boundingRectWithSize:maximumLabelSize
                                                       options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                    attributes:stringAttributes context:nil];

    self.myLabelHeightConstraint.constant = ceilf(newRect.size.height);

Ich habe alles auf dieser Seite ausprobiert und hatte immer noch einen Fall für ein UILabel, das nicht richtig formatiert war. Das tatsächliche Setzen des Attributtextes auf dem Etikett hat das Problem endgültig behoben.

kleine
quelle
1

Eine Sache, die mir aufgefallen ist, ist, dass das Rechteck, von dem ich zurückkommen (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)contextwürde, eine größere Breite haben würde als das, was ich eingegeben habe. Wenn dies passiert ist, wird meine Zeichenfolge abgeschnitten. Ich habe es so gelöst:

NSString *aLongString = ...
NSInteger width = //some width;            
UIFont *font = //your font;
CGRect rect = [aLongString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin)
                                     attributes:@{ NSFontAttributeName : font,
                                                   NSForegroundColorAttributeName : [UIColor whiteColor]}
                                        context:nil];

if(rect.size.width > width)
{
    return rect.size.height + font.lineHeight;
}
return rect.size.height;

Für etwas mehr Kontext; Ich hatte mehrzeiligen Text und versuchte, die richtige Höhe für die Anzeige zu finden. BoundRectWithSize gab manchmal eine Breite zurück, die größer war als angegeben. Wenn ich also meine Vergangenheit in der Breite und die berechnete Höhe für die Anzeige meines Textes verwendete, wurde diese angezeigt würde abschneiden. Nach dem Testen, wenn boundingRectWithSize die falsche Breite verwendet hat, betrug der Betrag, um den die Höhe verkürzt wurde, 1 Zeile. Daher würde ich prüfen, ob die Breite größer ist, und in diesem Fall die Zeilenhöhe der Schriftart hinzufügen, um genügend Speicherplatz bereitzustellen, um ein Abschneiden zu vermeiden.

odyth
quelle
Wie wirkt sich die Linienhöhe mit der Breite aus?
Roi Mulia
Die Linienhöhe von @RoiMulia hat keinen Einfluss auf die Breite. Ich habe meine Antwort aktualisiert, um mehr Kontext zu bieten, wie dies meinen Fehler behoben hat.
odyth
0
    NSAttributedString *attributedText =[[[NSAttributedString alloc]
                                          initWithString:joyMeComment.content
                                          attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:TextFont]}] autorelease];

    CGRect paragraphRect =
    [attributedText boundingRectWithSize:CGSizeMake(kWith, CGFLOAT_MAX)
                                 options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                 context:nil];
    contentSize = paragraphRect.size;

    contentSize.size.height+=10;
    label.frame=contentSize;

Wenn der Rahmen des Etiketts nicht 10 addiert, funktioniert diese Methode niemals! hoffe das kann dir helfen! Viel Glück.

MICHEAL LV
quelle
0
Add Following methods in ur code for getting correct size of attribute string 
1.
    - (CGFloat)findHeightForText:(NSAttributedString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
 {
    UITextView *textView = [[UITextView alloc] init];
    [textView setAttributedText:text];
    [textView setFont:font];
    CGSize size = [textView sizeThatFits:CGSizeMake(widthValue, FLT_MAX)];
    return size.height;

}}

2. Call on heightForRowAtIndexPath method
     int h = [self findHeightForText:attrString havingWidth:yourScreenWidth andFont:urFont];
Anny
quelle
In meinem Fall sollte ich die Berechnung für den Hintergrund-Thread durchführen, und die Verwendung von UI-Elementen ist nicht zulässig
Lubbo
0

Ich möchte meine Gedanken hinzufügen, da ich genau das gleiche Problem hatte.

Ich habe es verwendet, UITextViewda es eine schönere Textausrichtung hatte (begründen, was zu diesem Zeitpunkt nicht verfügbar war UILabel), aber um nicht interaktiv-nicht scrollbar zu "simulieren" UILabel, habe ich das vollständige Scrollen, Bouncen und die Benutzerinteraktion deaktiviert .

Das Problem war natürlich, dass der Text dynamisch war und die Breite zwar festgelegt wurde, die Höhe jedoch jedes Mal neu berechnet werden sollte, wenn ich einen neuen Textwert festlegte.

boundingRectWithSizeEs funktionierte überhaupt nicht gut für mich, soweit ich sehen konnte, UITextViewfügte ich oben einen Rand hinzu, der boundingRectWithSizenicht gezählt werden konnte. Daher war die Höhe, aus der abgerufen boundingRectWithSizewurde, kleiner als sie sein sollte.

Da der Text nicht schnell aktualisiert werden sollte, sondern nur für einige Informationen verwendet wird, die möglicherweise alle 2-3 Sekunden am häufigsten aktualisiert werden, habe ich mich für folgenden Ansatz entschieden:

/* This f is nested in a custom UIView-inherited class that is built using xib file */
-(void) setTextAndAutoSize:(NSString*)text inTextView:(UITextView*)tv
{
    CGFloat msgWidth = tv.frame.size.width; // get target's width

    // Make "test" UITextView to calculate correct size
    UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, msgWidth, 300)]; // we set some height, really doesn't matter, just put some value like this one.
    // Set all font and text related parameters to be exact as the ones in targeted text view
    [temp setFont:tv.font];
    [temp setTextAlignment:tv.textAlignment];
    [temp setTextColor:tv.textColor];
    [temp setText:text];

    // Ask for size that fits :P
    CGSize tv_size = [temp sizeThatFits:CGSizeMake(msgWidth, 300)];

    // kill this "test" UITextView, it's purpose is over
    [temp release];
    temp = nil;

    // apply calculated size. if calcualted width differs, I choose to ignore it anyway and use only height because I want to have width absolutely fixed to designed value
    tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, msgWidth, tv_size.height );
}

* Der obige Code wird nicht direkt von meiner Quelle kopiert, ich musste ihn anpassen / aus einer Reihe anderer Dinge entfernen, die für diesen Artikel nicht benötigt werden. Nehmen Sie es nicht für Copy-Paste-and-It-Will-Work-Code.

Offensichtlicher Nachteil ist, dass es für jeden Anruf eine Zuweisung und Freigabe hat.

Aber Vorteil ist , dass Sie vermeiden , je nach Kompatibilität zwischen dem, wie boundingRectWithSize zieht Text und berechnet seine Größe und Umsetzung von Text in Zeichnung UITextView(oder UILabeldie auch können Sie verwenden , ersetzen Sie einfach UITextViewmit UILabel). Alle "Fehler", die Apple möglicherweise hat, werden auf diese Weise vermieden.

PS Es scheint, dass Sie diese "Temperatur" nicht brauchen sollten UITextViewund einfach sizeThatFitsdirekt vom Ziel fragen können, aber das hat bei mir nicht funktioniert. Obwohl die Logik sagen würde, dass es funktionieren sollte und die Zuweisung / Freigabe von temporären UITextViewnicht erforderlich ist, war dies nicht der Fall . Aber diese Lösung funktionierte einwandfrei für jeden Text, den ich einfügen würde.

Sinisa
quelle
In meinem Fall sollte ich die Berechnung auf Hintergrund-Thread durchführen, und die Verwendung von UI-Elementen ist nicht erlaubt
Lubbo
0

Ok, also habe ich viel Zeit damit verbracht, dies zu debuggen. Ich fand heraus, dass die maximale Texthöhe, wie sie durch definiert ist boundingRectWithSize, Text von meinem anzeigen darfUITextView wurde, niedriger als die Rahmengröße war.

In meinem Fall ist der Frame höchstens 140pt, aber die UITextView toleriert höchstens 131pt Texte.

Ich musste das manuell herausfinden und die "echte" maximale Höhe fest codieren.

Hier ist meine Lösung:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSString *proposedText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:proposedText];
    CGRect boundingRect;
    CGFloat maxFontSize = 100;
    CGFloat minFontSize = 30;
    CGFloat fontSize = maxFontSize + 1;
    BOOL fit;
    NSLog(@"Trying text: \"%@\"", proposedText);
    do {
        fontSize -= 1;
        //XXX Seems like trailing whitespaces count for 0. find a workaround
        [attributedText addAttribute:NSFontAttributeName value:[textView.font fontWithSize:fontSize] range:NSMakeRange(0, attributedText.length)];
        CGFloat padding = textView.textContainer.lineFragmentPadding;
        CGSize boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFLOAT_MAX);
        boundingRect = [attributedText boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil];
        NSLog(@"bounding rect for font %f is %@; (max is %f %f). Padding: %f", fontSize, NSStringFromCGRect(boundingRect), textView.frame.size.width, 148.0, padding);
        fit =  boundingRect.size.height <= 131;
    } while (!fit && fontSize > minFontSize);
    if (fit) {
        self.textView.font = [self.textView.font fontWithSize:fontSize];
        NSLog(@"Fit!");
    } else {
        NSLog(@"No fit");
    }
    return fit;
}
Antzi
quelle