boundingRectWithSize per NSAttributedString che restituisce dimensioni errate


151

Sto cercando di ottenere il rettangolo per una stringa attribuita, ma la chiamata boundingRectWithSize non rispetta la dimensione in cui passo e restituisce un rettangolo con un'altezza di riga singola anziché un'altezza grande (è una stringa lunga). Ho sperimentato passando un valore molto grande per l'altezza e anche 0 come nel codice seguente, ma il valore restituito è sempre lo stesso.

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

È rotto o devo fare qualcos'altro per restituire un rettangolo per il testo a capo?


5
Ti capita di avere uno stile di paragrafo con troncamento / ritaglio lineBreakMode?
Danyowdee,

1
se stai leggendo questo perché UILabel misura / avvolge con una larghezza errata, dai un'occhiata a stackoverflow.com/questions/46200027/… . in particolare, impostando NSAllowsDefaultLineBreakStrategy su false all'avvio dell'app.
eric,

Risposte:


312

Sembra che tu non stia fornendo le opzioni corrette. Per avvolgere le etichette, fornire almeno:

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

Nota: se la larghezza del testo originale è inferiore a 300.f non ci sarà il ritorno a capo, quindi assicurati che la dimensione del bordo sia corretta, altrimenti otterrai comunque risultati errati.


25
Tutti hanno bisogno di guardare questa risposta, perché funziona. Forse è necessaria una documentazione più chiara per questo metodo e le stringhe attribuite in generale; non è ovvio dove devi cercare.
macserv,

31
Attenzione! Non sempre funziona! A volte la larghezza restituita è maggiore della larghezza del parametro size. Anche la documentazione relativa al metodo Apples afferma: "I vincoli specificati nel parametro size sono una guida per il renderer su come dimensionare la stringa. Tuttavia, il rettangolo di delimitazione effettivo restituito da questo metodo può essere maggiore dei vincoli se è necessario spazio aggiuntivo per renderizza l'intera stringa. "
Klaas,

75
L'API per NSAttributedString e boundingRectWithSize è assolutamente scioccante.
Malhal,

27
Un altro gotcha è che l'altezza (e presumibilmente la larghezza) restituita nel paragrafo Rect è quasi sempre un valore frazionario. Quindi potrebbe venire fuori dicendo 141.3 come altezza. È necessario utilizzare il risultato in ceilf(paragraphRect.size.height)modo che arrotonda per eccesso. Lo dimentico sempre e mi chiedo perché le mie etichette siano ancora ritagliate.
Jamone,

39
boundingRectWithSize:Avvolgo sempre i miei calcoli in CGRectIntegral () che CGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integers, in questo caso, arrotonderà l'altezza e la larghezza per garantire che non si verifichino ritagli se l'altezza o la larghezza sono un valore frazionario.
Runmad,

47

Per qualche motivo, boundingRectWithSize restituisce sempre dimensioni errate. Ho trovato una soluzione. Esiste un metodo per UItextView -sizeThatFits che restituisce la dimensione corretta per il set di testo. Quindi, invece di utilizzare boundingRectWithSize, crea un UITextView, con un frame casuale, e chiama la sua dimensione che si adatta con la rispettiva larghezza e altezza CGFLOAT_MAX. Restituisce la dimensione che avrà l'altezza corretta.

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

Se stai calcolando le dimensioni in un ciclo while, non dimenticare di aggiungerlo in un pool di rilascio automatico, poiché non vi sarà n numero di UITextView creato, la memoria di runtime dell'app aumenterà se non utilizziamo autoreleasepool.


1
Questo funziona Gli altri modi in cui ho provato non sono mai riuscito a lavorare. Penso che il mio problema derivi dalla complessità della stringa attribuita che stavo assegnando. Veniva da un file RTF e utilizzava una varietà di caratteri, interlinea, persino ombre, e nonostante usasse UsesLineFragmentOrigin e UsesFontLeading non riuscivo mai a ottenere un risultato sufficientemente alto e il problema era peggiore quanto più la stringa attribuita diventava grave. La mia ipotesi è che per stringhe relativamente semplici il metodo boundingRectWithSize potrebbe funzionare. Hanno davvero bisogno di un metodo migliore per lavorare con questo, imo.
John Bushnell,

non pensi che sia eccessivo creare un UITextViewnumero di volte superiore a quello heightForRowAtIndexPathche viene chiamato il metodo? ci vuole tempo
János,

Sono d'accordo con te @ János, ma questa è stata l'unica soluzione che ha funzionato per me.
shoan

tra l'altro ho chiesto la soluzione corretta: stackoverflow.com/questions/32495744/…
János

questa è l'unica soluzione che funziona. Questo problema ha 9 anni e Apple apparentemente non vuole risolvere questo o i suoi ingegneri sono troppo deboli per trovare una soluzione. Stiamo parlando di iOS 11, con lo stesso bug ancora presente.
Duck

31

Ed McManus ha sicuramente fornito una chiave per farlo funzionare. Ho trovato un caso che non funziona

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 non avrà l'altezza corretta. Si noti che anotherString (che viene aggiunto alla stringa ) è stato inizializzato senza un dizionario degli attributi. Questo è un inizializzatore legittimo per anotherString ma boundingRectWithSize: in questo caso non fornisce una dimensione precisa.


33
Grazie! Si scopre che OGNI parte di un NSAttributedString deve avere un dizionario impostato con almeno NSFontAttributeName e NSForegroundColorAttributeName impostati, se si desidera che boundingRectWithSize funzioni davvero! Non lo vedo documentato da nessuna parte.
Ben Wheeler,

3
Si noti che sembra anche che i diversi NSAttributedString che vengono combinati per crearne uno singolo debbano tutti utilizzare il dizionario SAME (e quindi lo stesso carattere) affinché il boundingRectWithSize funzioni correttamente!
Ben Wheeler,

1
Questa soluzione è molto simile a quella che uso per la mia categoria UILabel per "ridimensionare per adattarsi". La chiave per me per farlo funzionare era creare il rettangolo di delimitazione con altezza FLT_MAX. Senza quello, sembrerei ottenere un rettangolo per una sola riga.
dre1138,

+1 a tutto tondo. Questo mi ha davvero colpito, quindi grazie per averlo capito ragazzi.
Wex,

Ho avuto questo problema Ho usato spazi e newline NSMutableAttributedStringsenza attributi e avevo dimensioni errate.
vbezhenar,

28

La mia decisione finale dopo una lunga indagine: la
- boundingRectWithSizefunzione restituisce la dimensione corretta solo per la sequenza ininterrotta di caratteri! Nel caso in cui la stringa contenga spazi o qualcos'altro (chiamato da Apple "Alcuni dei glifi") - è impossibile ottenere l'effettiva dimensione di rettangolo necessaria per visualizzare il testo!
Ho sostituito gli spazi nelle mie stringhe con lettere e ho subito ottenuto il risultato corretto.

Apple dice qui: https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

"Questo metodo restituisce i limiti effettivi dei glifi nella stringa. Alcuni dei glifi (spazi, ad esempio) possono sovrapporsi ai vincoli di layout specificati dalla dimensione passata, quindi in alcuni casi il valore di larghezza del componente dimensione di il valore restituito CGRectpuò superare il valore di larghezza del parametro size. "

Quindi è necessario trovare un altro modo per calcolare il retto effettivo ...


Dopo un lungo processo di indagine è stata finalmente trovata la soluzione !!! Non sono sicuro che funzionerà bene per tutti i casi relativi UITextView, ma la cosa principale e importante è stata rilevata!

boundingRectWithSizefunzione e CTFramesetterSuggestFrameSizeWithConstraints(e molti altri metodi) calcoleranno la dimensione e la porzione di testo corrette quando si utilizza il rettangolo corretto. Ad esempio - UITextViewhas textView.bounds.size.width- e questo valore non è il rettangolo effettivo utilizzato dal sistema quando si attinge il testo UITextView.

Ho trovato un parametro molto interessante ed eseguito semplici calcoli nel codice:

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

E la magia funziona: tutti i miei testi sono stati calcolati corretti ora! Godere!


1
sì, ho notato anche questo, non mantiene i vincoli per la larghezza, ne esce sempre più grande. Non è davvero bello, hanno deprecato il metodo di lavoro e ora dobbiamo affrontare questa merda.
Boris Gafurov,

1
Signore, siete il mio nuovo eroe! Questo mi stava facendo impazzire. Sto impostando gli inserti textContainers, quindi ho dovuto usare textView.textContainer.size.width invece di textView.bounds.size.witdh.
GCBenson,

Non ho potuto votare abbastanza. Così strano che gli spazi non vengono considerati nei calcoli di delimitazione.
Chase Holland,

1
La sostituzione degli spazi con un personaggio fittizio come "3" restituisce l'altezza esatta
Abuzar Amin

1
questo ha salvato la mia carriera e il mio rapporto con il team QA. Ti devo un birraio !!
lucaslt89,

14

Swift four 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

Misurare il testo con CTFramesetter funziona meglio in quanto fornisce dimensioni intere e gestisce bene le emoji e altri caratteri unicode.


10

Non ho avuto fortuna con nessuno di questi suggerimenti. La mia stringa conteneva punti elenco unicode e sospetto che stessero causando dolore nel calcolo. Ho notato che UITextView stava gestendo bene il disegno, quindi ho guardato a quello per sfruttare il suo calcolo. Ho fatto quanto segue, che probabilmente non è ottimale come i metodi di disegno NSString, ma almeno è accurato. È anche leggermente più ottimale che inizializzare un UITextView solo per chiamare -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);

Potrebbero non essere stati i punti unicode, potrebbero essere stati degli spazi alla fine della stringa. Vedi la risposta sopra: stackoverflow.com/a/25941139/337934 .
SamB,

9

Nel caso in cui desideri ottenere un rettangolo di selezione troncando la coda, questa domanda può aiutarti.

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

9

Si scopre che OGNI parte di un NSAttributedString deve avere un dizionario impostato con almeno NSFontAttributeName e NSForegroundColorAttributeName impostati, se si desidera che boundingRectWithSize funzioni davvero!

Non lo vedo documentato da nessuna parte.


Grazie, questa è stata la soluzione per me, tranne che ho scoperto che era necessario solo NSFontAttributeName, non NSForegroundColorAttributeName
Marmoy

7

@warrenm Mi dispiace dire che il metodo framesetter non ha funzionato per me.

Questa funzione può aiutarci a determinare la dimensione del frame necessaria per un intervallo di stringhe di un NSAttributedString in iPhone / Ipad SDK per una determinata larghezza:

Può essere utilizzato per un'altezza dinamica delle celle UITableView

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

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


Non funziona per me su nessun dispositivo (iPhone 4 e 5). Entrambi su dispositivo con iOS7.1.2 e 4s Simulator con iOS8.3 :(
sanjana

Questo ha bisogno di importazione?
jose920405,

@KaranAlangat sì#import <CoreText/CoreText.h>
jose920405

7

Ho scoperto che la soluzione preferita non gestisce le interruzioni di riga.

Ho trovato che questo approccio funziona in tutti i casi:

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;

Nel mio caso dovrei fare il calcolo sul thread in background e l'uso degli elementi dell'interfaccia utente non è consentito
Lubbo,

4

Ho avuto lo stesso problema con il non ottenere una dimensione precisa usando queste tecniche e ho cambiato il mio approccio per farlo funzionare.

Ho una lunga stringa attribuita che ho cercato di inserire in una vista di scorrimento in modo che mostri correttamente senza essere troncato. Quello che ho fatto per far funzionare il testo in modo affidabile è stato quello di non impostare affatto l'altezza come un vincolo e invece ho permesso alle dimensioni intrinseche di prendere il controllo. Ora il testo viene visualizzato correttamente senza essere troncato e non devo calcolare l'altezza.

Suppongo che se avessi bisogno di ottenere l'altezza in modo affidabile, creerei una vista nascosta e questi vincoli e otterrei l'altezza del frame una volta applicati i vincoli.


2
Puoi aggiungere un esempio di codice per dimostrarlo? Grazie.
Nathan Buggia,

3

Sono un po 'in ritardo al gioco, ma ho cercato di trovare un modo per trovare il rettangolo di selezione che si adatterà attorno a una stringa attribuita per creare un anello di messa a fuoco come la modifica di un file nel Finder. tutto ciò che avevo provato falliva quando c'erano degli spazi alla fine della stringa o più spazi all'interno della stringa. boundingRectWithSizefallisce miseramente anche per questo CTFramesetterCreateWithAttributedString.

L'uso NSLayoutManagerdel seguente codice sembra fare il trucco in tutti i casi che ho trovato finora e restituisce un rettangolo che limita perfettamente la stringa. Bonus: se selezioni il testo i bordi della selezione vanno fino ai limiti del rettangolo restituito. Il codice seguente utilizza il layoutManager da a NSTextView.

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

CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container];

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

Funziona perfettamente su tutti i caratteri!


1

Ho avuto lo stesso problema, ma ho riconosciuto che l'altezza vincolata è stata impostata correttamente. Quindi ho fatto quanto segue:

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

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

Ho provato tutto su questa pagina e avevo ancora un caso per un UILabel che non stava formattando correttamente. L'impostazione dell'attributoText sull'etichetta ha finalmente risolto il problema.


1

Una cosa che stavo notando è che il retto da cui sarebbe tornato (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)contextavrebbe una larghezza maggiore di quello che ho passato. Quando questo è accaduto, la mia corda sarebbe stata troncata. L'ho risolto in questo modo:

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;

Per qualche altro contesto; Avevo un testo su più righe e stavo cercando di trovare l'altezza giusta per visualizzarlo. troncerebbe. Dal test quando boundingRectWithSize utilizzava la larghezza errata, la quantità che avrebbe reso l'altezza ridotta era di 1 riga. Quindi verificherei se la larghezza fosse maggiore e in tal caso aggiungere la linea fontHeight per fornire spazio sufficiente per evitare il troncamento.


Che effetto ha l'altezza della linea con Larghezza?
Roi Mulia,

L'altezza della linea @RoiMulia non influisce sulla larghezza. Ho aggiornato la mia risposta per fornire più contesto su come questo ha risolto il mio bug.
1818

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;

se il frame dell'etichetta non aggiunge 10 questo metodo non funzionerà mai! Spero che questo possa aiutarti! buona fortuna.


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

Nel mio caso dovrei fare il calcolo sul thread in background e l'uso degli elementi dell'interfaccia utente non è consentito
Lubbo,

0

Vorrei aggiungere i miei pensieri poiché ho avuto esattamente lo stesso problema.

Lo stavo usando UITextViewperché aveva un allineamento del testo più gradevole (giustificato, che al momento non era disponibile in UILabel), ma per "simulare" non interattivo-non scorrevole UILabel, disattivavo completamente lo scorrimento, il rimbalzo e l'interazione dell'utente .

Ovviamente, il problema era che il testo era dinamico e, sebbene la larghezza fosse fissa, l'altezza doveva essere ricalcolata ogni volta che avevo impostato un nuovo valore di testo.

boundingRectWithSizeper me non ha funzionato affatto bene, da quello che ho potuto vedere, è UITextViewstato aggiungere un margine in più che boundingRectWithSizenon avrebbe contato, quindi l'altezza recuperata boundingRectWithSizeera più piccola di quanto dovrebbe essere.

Dato che il testo non doveva essere aggiornato rapidamente, viene utilizzato solo per alcune informazioni che potrebbero aggiornarsi di più ogni 2-3 secondi, ho deciso il seguente approccio:

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

* Il codice sopra non viene copiato direttamente dalla mia fonte, ho dovuto modificarlo / cancellarlo da un sacco di altre cose non necessarie per questo articolo. Non prenderlo per copia-incolla-e-esso-funzionerà-codice.

Lo svantaggio evidente è che ha allocazione e rilascio, per ogni chiamata.

Ma il vantaggio è che si evita a seconda della compatibilità tra il modo in cui boundingRectWithSize disegna il testo e calcola le dimensioni e l'implementazione del disegno del testo UITextView(o UILabelche è possibile utilizzare anche solo UITextViewcon Sostituisci UILabel). In questo modo si evitano eventuali "bug" che Apple potrebbe avere.

PS Sembrerebbe che non dovresti aver bisogno di questo "temp" UITextViewe puoi semplicemente chiedere sizeThatFitsdirettamente dal target, tuttavia non ha funzionato per me. Sebbene la logica direbbe che dovrebbe funzionare e allocare / rilasciare il temporaneo UITextViewnon è necessario, non lo ha fatto. Ma questa soluzione ha funzionato perfettamente per qualsiasi testo che avrei inserito.


Nel mio caso dovrei fare il calcolo sul thread in background e l'uso degli elementi dell'interfaccia utente non è consentito
Lubbo,

0

Ok, quindi ho trascorso molto tempo a eseguire il debug di questo. Ho scoperto che l'altezza massima del testo definita da boundingRectWithSizeconsentito per visualizzare il testo dal mio UITextViewera inferiore alla dimensione della cornice.

Nel mio caso il frame è al massimo di 140 pt, ma UITextView tollera i testi al massimo di 131 pt.

Ho dovuto capirlo manualmente e codificare l'altezza massima "reale".

Ecco la mia soluzione:

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