iOS: Mehrzeiliges UILabel im automatischen Layout

183

Ich habe Probleme beim Versuch, mit Auto Layout ein sehr grundlegendes Layoutverhalten zu erzielen. Mein View Controller sieht in IB folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

Das Top-Label ist das Titel-Label, ich weiß nicht, wie viele Zeilen es sein wird. Ich benötige die Titelbezeichnung, um alle Textzeilen anzuzeigen. Ich brauche auch die beiden anderen Etiketten und das kleine Bild, das direkt unter dem Titel angeordnet ist, wie groß es auch sein mag. Ich habe vertikale Abstandsbeschränkungen zwischen den Beschriftungen und dem kleinen Bild sowie eine obere Abstandsbeschränkung zwischen der Titelbeschriftung und ihrer Übersicht und eine untere Abstandsbeschränkung zwischen dem kleinen Bild und seiner Übersicht festgelegt. Das weiße UIView hat keine Höhenbeschränkung, daher sollte es sich vertikal dehnen, um seine Unteransichten zu enthalten. Ich habe die Anzahl der Zeilen für die Titelbezeichnung auf 0 gesetzt.

Wie kann ich die Größe der Titelbezeichnung an die Anzahl der für die Zeichenfolge erforderlichen Zeilen anpassen? Nach meinem Verständnis kann ich keine setFrame-Methoden verwenden, da ich das automatische Layout verwende. Und ich muss das automatische Layout verwenden, da diese anderen Ansichten unter der Titelbezeichnung bleiben müssen (daher die Einschränkungen).

Wie kann ich das erreichen?

James Harpe
quelle
3
Ich kämpfe auch mit einem ähnlichen Problem. Aber ich kämpfe immer noch darum, dass das obere Etikett seine Höhe dynamisch an den Inhalt anpasst. Wie haben Sie das erreicht?
Jbandi
1
Bitte erwägen Sie, die Antwort von @ mwhuss als die akzeptierte zu markieren.
Jemmons
1
Haben Sie das gewünschte Ergebnis erzielt?
Aniruddha
Überprüfen Sie dies, Sie müssen keine einzelne Codezeile Stackoverflow.com/a/36862795/4910767
Badal Shah

Antworten:

254

Die Verwendung -setPreferredMaxLayoutWidthauf dem UILabelund Autolayout sollte den Rest erledigen.

[label setPreferredMaxLayoutWidth:200.0];

Weitere Informationen finden Sie in der UILabel-Dokumentation zu PreferredMaxLayoutWidth .

Aktualisieren:

Sie müssen die heightEinschränkung im Storyboard nur auf festlegen Greater than or equal to, keine Einstellung für PreferredMaxLayoutWidth.

mwhuss
quelle
8
Netter, trotzdem, um dies im Interface Builder zu tun?
Sjoerd Perfors
12
Setzen Sie auch die Höhenbeschränkung in IB auf größer als oder gleich ".
jowie
10
Erinnerung: Bitte denken Sie daran, die numberOfLines-Eigenschaft festzulegen.
Li Fumin
2
Ja, verwenden Sie die maximale Breite.
mwhuss
4
Unter stackoverflow.com/a/29180323/1529675 finden Sie Informationen zum Ermitteln preferredMaxLayoutWidthund Anzeigen von devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html für eine kurze, aber informative Beschreibung mit animierte GIFs (nicht mein Artikel, ich habe ihn über Google gefunden)
tboyce12
213

Erweitern Sie die Anzahl der Zeilen Ihres Beschriftungssatzes auf 0 und, was noch wichtiger ist, für die Höhe des automatischen Layoutsatzes auf> = x. Das automatische Layout erledigt den Rest. Sie können auch Ihre anderen Elemente basierend auf dem vorherigen Element enthalten, um sie dann korrekt zu positionieren.

Auto-Layout

Charlie Wu
quelle
Wenn diese Methode bei Ihnen nicht funktioniert, lesen Sie bitte die Antwort von Cory Imdieke, um sicherzustellen, dass nachfolgende Ansichten nicht verhindern, dass das automatische Layout die Größe der (möglicherweise) mehrzeiligen Ansicht ändert.
Tom Howard
1
Dies ist die einzige Antwort, die für mich in einer Tabellenansichtszelle funktioniert hat. Funktioniert auch mit einer festen Anzahl von Zeilen (anstelle von 0 / unbegrenzt)
bgolson
Dies funktionierte bei mir mit einem explizit bevorzugten maximalen Breitensatz nicht. Stellen Sie daher sicher, dass dieser in IB bei Verwendung dieser Technik auf automatisch eingestellt ist. Prost
jmc
Das Einstellen der automatischen maximalen Breite ist nur in iOS8 verfügbar. Nach meinem Verständnis wird die Höhe auch automatisch angepasst, und meiner Erfahrung nach müssen Sie keine Höhenbeschränkung festlegen, damit sie funktioniert. Dies funktionierte bei mir mit einer expliziten Breite.
Tony
1
Das hat bei mir nicht funktioniert. Ich glaube, stackoverflow.com/a/13032251/1529675 ist die richtige Antwort.
tboyce12
48

Quelle: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html

Intrinsische Inhaltsgröße von mehrzeiligem Text

Die intrinsische Inhaltsgröße von UILabel und NSTextField ist für mehrzeiligen Text nicht eindeutig. Die Höhe des Textes hängt von der Breite der Linien ab, die beim Lösen der Einschränkungen noch festgelegt werden muss. Um dieses Problem zu lösen, verfügen beide Klassen über eine neue Eigenschaft namens PreferredMaxLayoutWidth, die die maximale Zeilenbreite für die Berechnung der intrinsischen Inhaltsgröße angibt.

Da wir diesen Wert normalerweise nicht im Voraus kennen, müssen wir einen zweistufigen Ansatz wählen, um dies richtig zu machen. Zuerst lassen wir das automatische Layout seine Arbeit erledigen, und dann verwenden wir den resultierenden Rahmen im Layoutdurchlauf, um die bevorzugte maximale Breite zu aktualisieren und das Layout erneut auszulösen.

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

Der erste Aufruf von [super layoutSubviews] ist erforderlich, damit das Label seinen Frame-Set erhält, während der zweite Aufruf erforderlich ist, um das Layout nach der Änderung zu aktualisieren. Wenn wir den zweiten Aufruf weglassen, wird ein NSInternalInconsistencyException-Fehler angezeigt, da wir Änderungen am Layout-Durchlauf vorgenommen haben, die eine Aktualisierung der Einschränkungen erfordern, das Layout jedoch nicht erneut ausgelöst haben.

Wir können dies auch in einer Label-Unterklasse selbst tun:

@implementation MyLabel
- (void)layoutSubviews
{
    self.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

In diesem Fall müssen wir nicht zuerst [super layoutSubviews] aufrufen, da beim Aufruf von layoutSubviews bereits ein Frame auf dem Etikett selbst vorhanden ist.

Um diese Anpassung von der View Controller-Ebene aus vorzunehmen, schließen wir uns viewDidLayoutSubviews an. Zu diesem Zeitpunkt sind die Frames des ersten Auto-Layout-Durchlaufs bereits festgelegt, und wir können sie verwenden, um die bevorzugte maximale Breite festzulegen.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

Stellen Sie schließlich sicher, dass Sie keine explizite Höhenbeschränkung für das Etikett haben, die eine höhere Priorität als die Priorität der Inhaltskomprimierungsbeständigkeit des Etiketts hat. Andernfalls wird die berechnete Höhe des Inhalts übertroffen. Stellen Sie sicher, dass Sie alle Einschränkungen überprüfen, die sich auf die Höhe des Etiketts auswirken können.

Anton Matosov
quelle
danke danke danke nicht nur für den Code, sondern vor allem für die Erklärung und die Beispiele, wie dies nicht nur in einer Unterklasse, sondern auch vom View Controller aus gemacht werden kann.
Dschibuti33
Ich versuche dies in meinem View Controller und viewDidLayoutSubviews wird nach viewWillAppear und viewDidAppear aufgerufen. Nach viewDidAppear blinkt, wenn die Größe der Beschriftungen korrekt geändert wird. Gibt es eine Möglichkeit, dies zu vermeiden? Gibt es eine Möglichkeit, die Größenänderung abzuschließen, bevor der Benutzer etwas sieht? In meinem Fall befinden sich meine mehrzeiligen Beschriftungen in einer tableHeaderView. Daher ändere ich die Höhe der Kopfzeilenansicht anhand der Beschriftungen anhand dieses Beispiels ( stackoverflow.com/questions/20982558/… )
djibouti33
@ djibouti33 können Sie bitte den Mindestbeispielcode angeben (z. B. im Format von Github Repo), damit ich Ihr Problem leicht überprüfen kann?
Anton Matosov
Danke @Anton. Unser Design hat seitdem von einer UITableView zu einer UICollectionView gewechselt, und zum Glück habe ich dieses Verhalten nicht gesehen. Wenn ich in meinem Git-Verlauf ein kleines Beispielprojekt erstellen kann, werde ich es Sie wissen lassen.
Dschibuti33
Ein Problem für mich, seit ich angefangen habe, auf einer iOS 9-Sim zu laufen, aber als ich auf iOS 8 getestet habe, haben die Probleme begonnen. Ich muss die maximale Breite manuell einstellen.
Naz
17

Ich habe nur mit genau diesem Szenario gekämpft, aber mit einigen weiteren Ansichten, die nach Bedarf in der Größe geändert und nach unten verschoben werden mussten. Es hat mich verrückt gemacht, aber ich habe es endlich herausgefunden.

Hier ist der Schlüssel: Interface Builder fügt gerne zusätzliche Einschränkungen hinzu, wenn Sie Ansichten hinzufügen und verschieben, und Sie bemerken dies möglicherweise nicht. In meinem Fall hatte ich eine Ansicht auf halber Höhe, die eine zusätzliche Einschränkung aufwies, die die Größe zwischen der Ansicht und der Übersicht festlegte und sie im Grunde auf diesen Punkt fixierte. Das bedeutete, dass nichts darüber größer werden konnte, weil es gegen diese Einschränkung verstoßen würde.

Eine einfache Möglichkeit, um festzustellen, ob dies der Fall ist, besteht darin, die Größe des Etiketts manuell zu ändern. Lässt IB Sie es anbauen? Wenn ja, bewegen sich die folgenden Beschriftungen wie erwartet? Stellen Sie sicher, dass Sie beide Optionen aktiviert haben, bevor Sie die Größe ändern, um zu sehen, wie Ihre Einschränkungen Ihre Ansichten verschieben:

IB-Menü

Wenn die Ansicht nicht funktioniert, folgen Sie den Ansichten darunter und stellen Sie sicher, dass in einer von ihnen kein oberster Bereich für die Überwachung der Einschränkungen vorhanden ist. Stellen Sie dann einfach sicher, dass die Option für die Anzahl der Zeilen für das Etikett auf 0 gesetzt ist und sich um den Rest kümmert.

Cory Imdieke
quelle
Ja, nachdem ich viel damit herumgespielt hatte, konnte ich den gewünschten Effekt erzielen. Sie müssen nur sehr sorgfältig über die gewünschten Einschränkungen nachdenken. Es hilft nicht, dass IB ständig Einschränkungen einführt, die Sie nicht erwarten.
James Harpe
5

Ich finde, Sie brauchen Folgendes:

  • Eine Top-Einschränkung
  • Eine führende Einschränkung (z. B. linke Seite)
  • Eine nachfolgende Einschränkung (z. B. rechte Seite)
  • Stellen Sie die Priorität für das Umarmen von Inhalten horizontal auf niedrig ein, damit der angegebene Bereich ausgefüllt wird, wenn der Text kurz ist.
  • Stellen Sie den Komprimierungswiderstand für Inhalte horizontal auf niedrig ein, damit er umbrochen wird, anstatt zu versuchen, breiter zu werden.
  • Setzen Sie die Anzahl der Zeilen auf 0.
  • Stellen Sie den Zeilenumbruchmodus auf Zeilenumbruch ein.
Chris
quelle
Dies sollte in xcode9 funktionieren. Ich musste nichts mit der Beschränkung der Etikettenhöhe tun (ich habe keine): Sie funktioniert in letzter Zeit sofort, wenn die Beschriftung eingeschränkt wurde (nachlaufend, führend, oben und unten). uikit erledigt den Rest
Anton Tropashko
Die inhaltliche Priorität war mein Problem. Vielen Dank, dass Sie mich auf diese Eigenschaft sowie die Druckfestigkeit aufmerksam gemacht haben. Sie haben mir geholfen, ein stundenlanges Problem zu lösen.
David Gay
0

Keine der verschiedenen Lösungen in den vielen Themen zu diesem Thema hat für meinen Fall perfekt funktioniert (x dynamische mehrzeilige Beschriftungen in dynamischen Tabellenansichtszellen).

Ich habe einen Weg gefunden, es zu tun:

Nachdem Sie die Einschränkungen für Ihr Etikett festgelegt und dessen mehrzeilige Eigenschaft auf 0 gesetzt haben, erstellen Sie eine Unterklasse von UILabel. Ich habe mein AutoLayoutLabel angerufen:

@implementation AutoLayoutLabel

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

@end
Tanguy G.
quelle
0

Ich habe eine UITableViewCell mit einem Textumbruchetikett. Ich habe Textumbruch wie folgt gearbeitet.

1) Stellen Sie die UILabel-Einschränkungen wie folgt ein.

Geben Sie hier die Bildbeschreibung ein

2) Stellen Sie Nr. von Zeilen auf 0.

3) UITabelViewCell wurde eine UILabel-Höhenbeschränkung hinzugefügt.

@IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!

4) Auf UITableViewCell:

priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5
AG
quelle
-12

Eine Möglichkeit, dies zu tun ... Wenn die Textlänge zunimmt, versuchen Sie, die Schriftgröße des Beschriftungstextes mithilfe von zu ändern (zu verringern)

Label.adjustsFontSizeToFitWidth = YES;
user1662392
quelle
1
Ich möchte, dass die verschiedenen Instanzen des View Controllers ein einheitliches Erscheinungsbild haben, daher möchte ich die Schriftgröße nicht ändern.
James Harpe