2012-10-31 1 views
29

Ho una vista che contiene solo un UILabel. Questa etichetta contiene testo multilinea. Il genitore ha una larghezza variabile che può essere ridimensionata con un gesto pan. Il mio problema è che quando eseguo questo ridimensionamento, UILabel non ricalcola l'altezza in modo tale che tutto il contenuto sia ancora visibile, semplicemente lo interrompe.Autolayout iOS: problema con UILabels in una vista genitore di ridimensionamento

sono riuscito a risolvere il problema con un po 'di un hack, ma è terribilmente lento per eseguire:

- (void)layoutSubviews { 

    CGSize labelSize = [self.labelDescription sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)]; 

    if (self.constraintHeight) { 
     [self removeConstraint:self.constraintHeight]; 
    } 

    self.constraintHeight = [NSLayoutConstraint constraintWithItem:self.labelDescription attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:labelSize.height]; 

    [self addConstraint:self.constraintHeight]; 

    [super layoutSubviews]; 
} 

Non dovrebbe succedere automaticamente con autolayout?

EDIT

La struttura del mio punto di vista è:

UIScrollView 
---> UIView 
    ---> UILabel 

Qui ci sono i vincoli sul UIScrollView:

<NSLayoutConstraint:0x120c4860 H:|-(>=32)-[DescriptionView:0x85c81c0] (Names: '|':UIScrollView:0x85db650)>, 
<NSLayoutConstraint:0x120c48a0 H:|-([email protected])-[DescriptionView:0x85c81c0] priority:900 (Names: '|':UIScrollView:0x85db650)>, 
<NSLayoutConstraint:0x120c48e0 H:[DescriptionView:0x85c81c0(<=576)]>, 
<NSLayoutConstraint:0x120c4920 H:[DescriptionView:0x85c81c0]-(>=32)-| (Names: '|':UIScrollView:0x85db650)>, 
<NSLayoutConstraint:0x120c4960 H:[DescriptionView:0x85c81c0]-([email protected])-| priority:900 (Names: '|':UIScrollView:0x85db650)>, 
<NSLayoutConstraint:0x8301450 DescriptionView:0x85c81c0.centerX == UIScrollView:0x85db650.centerX>, 

Ecco i vincoli sulla UIView:

<NSLayoutConstraint:0x85c4580 V:|-(0)-[UILabel:0x85bc7b0] (Names: '|':DescriptionView:0x85c81c0)>, 
<NSLayoutConstraint:0x85c45c0 H:|-(0)-[UILabel:0x85bc7b0] (Names: '|':DescriptionView:0x85c81c0)>, 
<NSLayoutConstraint:0x85c9f80 UILabel:0x85bc7b0.trailing == DescriptionView:0x85c81c0.trailing>, 
<NSLayoutConstraint:0x85c9fc0 UILabel:0x85bc7b0.centerY == DescriptionView:0x85c81c0.centerY> 

Lo stesso UILabel non ha vincoli su di esso, oltre a bloccarlo ai bordi della sua madre

+0

Quali sono i tuoi limiti sull'etichetta e sul contenitore in cui si trova? – larsacus

+0

Vincoli aggiunti al post originale – simon

+0

Quindi l'unico vincolo che hai impostato sull'etichetta è che riempie il suo contenitore sia in orizzontale che in verticale? Il testo si interrompe quando il contenitore ha una dimensione specifica o taglia sempre il testo indipendentemente dalla grandezza del contenitore? – larsacus

risposta

26

Ho risolto questo problema dopo aver sollevato un bug con Apple. Il problema che testo plurimo richiede un approccio a due passaggi al layout e tutto si basa sulla struttura preferredMaxLayoutWidth

Ecco il codice pertinente che deve essere aggiunto a un controller visualizzazione contenente un'etichetta multilinea:

- (void)viewWillLayoutSubviews 
{ 
    // Clear the preferred max layout width in case the text of the label is a single line taking less width than what would be taken from the constraints of the left and right edges to the label's superview 
    [self.label setPreferredMaxLayoutWidth:0.]; 
} 

- (void)viewDidLayoutSubviews 
{ 
    // Now that you know what the constraints gave you for the label's width, use that for the preferredMaxLayoutWidth—so you get the correct height for the layout 
    [self.label setPreferredMaxLayoutWidth:[self.label bounds].size.width]; 

    // And then layout again with the label's correct height. 
    [self.view layoutSubviews]; 
} 
+1

Grazie per aver condiviso questa soluzione - assolutamente inconcepibile! – matt

+3

Tuttavia, ho provato questa soluzione e trovo che l'impostazione del preferitoMaxLayoutWidth dell'etichetta abbia ancora un ciclo di layout in ritardo. Quindi a volte si adatta al testo e talvolta l'ultimo paio di parole del testo vengono rilasciate dall'etichetta (non diventa abbastanza alta). – matt

+0

è stato risolto per 6.1? – midas06

41

Ok, l'ho finalmente inchiodato. La soluzione è impostare preferredMaxLayoutWidth in viewDidLayoutSubviews, ma solo dopo il il primo ciclo di layout. È possibile organizzare questo semplicemente inviando in modo asincrono sul thread principale. Quindi:

- (void)viewDidLayoutSubviews { 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     self.theLabel.preferredMaxLayoutWidth = self.theLabel.bounds.size.width; 
    }); 
} 

In questo modo, non si imposta preferredMaxLayoutWidth fino dopo larghezza della etichetta è stata correttamente impostata dai suoi vincoli Superview legate.

esempio di funzionamento del progetto qui:

https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch23p673selfSizingLabel4/p565p579selfSizingLabel/ViewController.m

EDIT: Un altro approccio! Sottoclasse UILabel e ignorare layoutSubviews:

- (void) layoutSubviews { 
    [super layoutSubviews]; 
    self.preferredMaxLayoutWidth = self.bounds.size.width; 
} 

Il risultato è un'etichetta auto-dimensionamento - cambia automaticamente la sua altezza per il suo contenuto non importa come cambia la sua larghezza (assumendo la sua larghezza è cambiato da vincoli/layout).

+0

Funziona bene. Grazie! – lichen19853

+1

@ lichen19853 nota che questa soluzione è necessaria solo nella curiosa situazione dell'OP, in cui una superview ridimensiona e modifica la larghezza dell'etichetta tramite i vincoli. Se modifichi direttamente la larghezza dell'etichetta, ad es. cambiando il suo limite di larghezza nel codice, puoi semplicemente cambiare la sua 'preferredMaxLayoutWidth' lì e poi. – matt

+2

Per quanto ne so, questa soluzione non funziona quando la vista genitore cresce di nuovo - le etichette non si espandono per occupare un nuovo spazio disponibile. – simon

4

In Xcode 6.1 per iOS 7/8, sono riuscito a farlo funzionare impostando semplicemente preferredMaxLayoutWidth in un metodo setter chiamato per visualizzare il testo dell'etichetta. Immagino sia stato impostato su 0 per cominciare. Esempio di seguito, dove self.teachPieceLabel è l'etichetta.L'etichetta è cablata con vincoli e altre etichette in una vista in Interface Builder.

- (void)setTeachPieceText:(NSString *)teachPieceText { 
     self.teachPieceLabel.text = teachPieceText; 
     [self.teachPieceLabel setPreferredMaxLayoutWidth:[self.teachPieceLabel bounds].size.width]; 
}