UILabel sizeToFit funktioniert nicht mit Autolayout ios6

152

Wie soll ich programmgesteuert (und in welcher Methode) ein UILabel konfigurieren, dessen Höhe von seinem Text abhängt? Ich habe versucht, es mit einer Kombination aus Storyboard und Code einzurichten, aber ohne Erfolg. Jeder empfiehlt sizeToFitbeim Einstellen lineBreakModeund numberOfLines. Doch egal , ob ich diesen Code setzte in viewDidLoad:, viewDidAppear:oder viewDidLayoutSubviewsich kann es nicht an der Arbeit. Entweder mache ich das Feld zu klein für langen Text und es wächst nicht, oder ich mache es zu groß und es schrumpft nicht.

Circuitlego
quelle
FWIW den gleichen Code: Ich musste nicht label.sizeToFit()in Xcode / viewController verwenden, die Einschränkungen waren genug. hat das Label nicht in Playground erstellt . Bisher habe ich es nur auf dem Spielplatz gefundenlabel.sizeToFit()
Honey

Antworten:

407

Bitte beachten Sie, dass die Lösung von Matt in den meisten Fällen wie erwartet funktioniert. Aber wenn es bei Ihnen nicht funktioniert, lesen Sie bitte weiter.

Um die Größe Ihres Etiketts automatisch zu ändern, müssen Sie folgende Schritte ausführen:

  1. Legen Sie Layoutbeschränkungen für die Beschriftung fest
  2. Stellen Sie die Höhenbeschränkung mit niedriger Priorität ein. Es sollte niedriger als ContentCompressionResistancePriority sein
  3. Setzen Sie numberOfLines = 0
  4. Stellen Sie ContentHuggingPriority höher als die Höhenpriorität des Etiketts ein
  5. Legen Sie PreferredMaxLayoutWidth für die Beschriftung fest. Dieser Wert wird vom Etikett verwendet, um seine Höhe zu berechnen

Beispielsweise:

self.descriptionLabel = [[UILabel alloc] init];
self.descriptionLabel.numberOfLines = 0;
self.descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.descriptionLabel.preferredMaxLayoutWidth = 200;

[self.descriptionLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.descriptionLabel];

NSArray* constrs = [NSLayoutConstraint constraintsWithVisualFormat:@"|-8-[descriptionLabel_]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)];
[self addConstraints:constrs];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[descriptionLabel_]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
[self.descriptionLabel addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[descriptionLabel_(220@300)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];

Verwenden des Interface Builder

  1. Richten Sie vier Einschränkungen ein. Die Höhenbeschränkung ist obligatorisch. Geben Sie hier die Bildbeschreibung ein

  2. Gehen Sie dann zum Attributinspektor des Etiketts und setzen Sie die Anzahl der Zeilen auf 0. Geben Sie hier die Bildbeschreibung ein

  3. Wechseln Sie zum Größeninspektor des Etiketts und erhöhen Sie die vertikale ContentHuggingPriority und die vertikale ContentCompressionResistancePriority.
    Geben Sie hier die Bildbeschreibung ein

  4. Wählen Sie die Höhenbeschränkung aus und bearbeiten Sie sie.
    Geben Sie hier die Bildbeschreibung ein

  5. Verringern Sie die Priorität der Höhenbeschränkung.
    Geben Sie hier die Bildbeschreibung ein

Genießen. :) :)

Mark Kryzhanouski
quelle
4
JA! Dies ist genau die Antwort, nach der ich gesucht habe. Das einzige, was ich tun musste, war, contentHuggingPriority und contentCompressionResistancePriority auf erforderlich zu setzen. Ich könnte alles in IB machen! Ich danke dir sehr.
Circuitlego
43
Du überdenkst das. Stellen Sie entweder preferredMaxLayoutWidthdie Breite des Etiketts ein oder stecken Sie es fest (oder die rechte und linke Seite). Das ist alles! Es wächst und schrumpft dann automatisch vertikal, um dem Inhalt zu entsprechen.
Matt
Ich musste die PreferredMaxLayoutWidth-Eigenschaft festlegen und die Breite des Etiketts anheften. Funktioniert wie ein Zauber :)
MartinMoizard
bevorzugtMaxLayoutWidth und / oder Fixieren der linken und rechten Seite ist nicht absolut. Die Prioritäten für das Umarmen und Komprimieren von Inhalten funktionieren immer, solange ihre Prioritäten höher sind als die übrigen Einschränkungen.
Eric Alford
2
^ Und ich habe endlich herausgefunden, warum, könnte erwähnenswert sein, wenn Sie die Zelle am unteren Rand der Ansicht haben, müssen Sie die Priorität der Einschränkung "nach unten" auf weniger als 1000 setzen. Ich habe sie auf 750 und gesetzt alles funktioniert perfekt. Ich denke, es hat die Aussicht geschrumpft.
Mathijs Segers
65

Wenn in iOS 6 bei Verwendung von Autolayout die Seiten (oder die Breite) und die Oberseite eines UILabels fixiert sind, wird es automatisch vertikal vergrößert und verkleinert, um dem Inhalt zu entsprechen, ohne Code und ohne Beeinträchtigung der Kompressionsbeständigkeit oder was auch immer. Es ist ganz einfach.

In komplexeren Fällen legen Sie einfach die Beschriftungen fest preferredMaxLayoutWidth.

In jedem Fall geschieht das Richtige automatisch.

matt
quelle
12
Sie müssen auch sicherstellen, dass numberOfLines auf 0 gesetzt ist, da Sie sonst keinen Wrapping erhalten.
Devongovett
2
Das hat mir nicht gereicht. Benötigte auch Punkt 2 aus @ Marks Antwort.
Danyal Aytekin
Ist es auch möglich, das Etikett automatisch in mehreren Zeilen wachsen zu lassen, ohne Codes zu schreiben?
Noor
Dies ist viel weniger kompliziert als die akzeptierte Antwort. Ich habe zwei Beispiele ( hier und hier ) gemacht, die diese Antwort veranschaulichen.
Suragch
@Suragch Mit Einschränkungen funktioniert dies. Aber es funktioniert nicht für den gleichen Code in Playground . Auf dem Spielplatz müssen Sie habenlabel.sizeToFit()
Honey
42

Obwohl die Frage programmgesteuert lautet, nachdem ich auf dasselbe Problem gestoßen bin und lieber in Interface Builder arbeite, hielt ich es für nützlich, die vorhandenen Antworten mit einer Interface Builder-Lösung zu ergänzen.

Das erste ist zu vergessen sizeToFit. Das automatische Layout übernimmt dies in Ihrem Namen basierend auf der Größe des Inhalts.

Das Problem ist daher, wie ein Etikett mit dem automatischen Layout an den Inhalt angepasst werden kann. Insbesondere - weil die Frage es erwähnt - Höhe. Beachten Sie, dass für die Breite dieselben Prinzipien gelten.

Beginnen wir also mit einem Beispiel für UILabel, dessen Höhe auf 41 Pixel hoch eingestellt ist:

Geben Sie hier die Bildbeschreibung ein

Wie Sie oben auf dem Bildschirm sehen können, "This is my text"ist oben und unten eine Polsterung vorhanden. Das ist ein Auffüllen zwischen der Höhe des UILabels und seinem Inhalt, dem Text.

Wenn wir die App im Simulator ausführen, sehen wir sicher dasselbe:

Geben Sie hier die Bildbeschreibung ein

Wählen Sie nun das UILabel im Interface Builder aus und sehen Sie sich die Standardeinstellungen im Größeninspektor an:

Geben Sie hier die Bildbeschreibung ein

Beachten Sie die oben hervorgehobene Einschränkung. Das ist die Priorität von Content Hugging . Wie Erica Sadun es im ausgezeichneten iOS Auto Layout Demystified beschreibt , ist dies:

Die Art und Weise, wie eine Ansicht es bevorzugt, zusätzliche Auffüllungen um ihren Kerninhalt zu vermeiden

Für uns mit dem UILabel ist der Text der Kerninhalt.

Hier kommen wir zum Kern dieses Grundszenarios. Wir haben unserer Textbezeichnung zwei Einschränkungen gegeben. Sie widersprechen sich. Man sagt "die Höhe muss gleich 41 Pixel hoch sein" . Der andere sagt „hug den Blick auf seinen Inhalt , so dass wir keine zusätzliche Polsterung haben“ . In unserem Fall sollten Sie die Ansicht an den Text anpassen, damit wir keine zusätzliche Polsterung haben.

Bei Auto Layout mit zwei verschiedenen Anweisungen, die unterschiedliche Aufgaben ausführen, muss die Laufzeit nun die eine oder andere auswählen. Es kann nicht beides. Das UILabel kann nicht beide 41 Pixel hoch sein und hat keine Polsterung.

Die Lösung erfolgt durch Angabe der Priorität. Ein Befehl muss eine höhere Priorität haben als der andere. Wenn beide Anweisungen unterschiedliche Aussagen machen und dieselbe Priorität haben, tritt eine Ausnahme auf.

Also lass es uns versuchen. Meine Höhenbeschränkung hat eine Priorität von 1000 , was erforderlich ist . Die Höhe des Inhalts beträgt 250 , was schwach ist . Was passiert, wenn wir die Priorität der Höhenbeschränkung auf 249 reduzieren ?

Geben Sie hier die Bildbeschreibung ein

Jetzt können wir sehen, wie die Magie beginnt. Versuchen wir es in der Sim:

Geben Sie hier die Bildbeschreibung ein

Genial! Content Hugging erreicht. Nur weil die Höhenpriorität 249 geringer ist als die Inhaltspriorität 250 . Grundsätzlich sage ich: "Die Höhe, die ich hier spezifiziere, ist weniger wichtig als die, die ich für das Umarmen von Inhalten angegeben habe . " So gewinnt der Inhalt, der umarmt.

Unter dem Strich kann das Anpassen der Beschriftung an den Text so einfach sein wie das Festlegen der Höhen- oder Breitenbeschränkung und das korrekte Festlegen dieser Priorität in Verbindung mit der inhaltlichen Prioritätsbeschränkung dieser Achse.

Wird das Äquivalent für Breite als Übung für den Leser tun!

Max MacLeod
quelle
3
Vielen Dank für Ihre tolle Erklärung! Schließlich verstehe ich Content Hugging Priority.
Kernix
Könnten Sie bitte auch erklären, was die Priorität der Inhaltskomprimierungsbeständigkeit ist? Vielen Dank!
Kernix
2
Excelente Antwort Max! Gibt es eine Möglichkeit, die Anzahl der so arbeitenden Leitungen zu begrenzen?
Rafaeljuzo
7

In IOS7 bemerkte sizeToFit funktionierte auch nicht - vielleicht hilft Ihnen die Lösung auch

[textView sizeToFit];
[textView layoutIfNeeded];
Nick Wood
quelle
1
@ Rambatino Das funktioniert bei mir. Fügen layoutIfNeededSie hinzu, wenn Sie brauchen setNeedsUpdateConstraints. Aber die Situation könnte etwas anders sein.
Gon
Das hat bei mir am besten funktioniert, als ich einen Popover gemacht habe. Ich musste allerdings zuerst layoutIfNeeded machen
Jason
4

Eine weitere Option, um sicherzustellen, dass die bevorzugte MaxLayoutWidth des Etiketts mit der Breite des Etiketts synchronisiert bleibt:

#import "MyLabel.h"

@implementation MyLabel

-(void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];

    // This appears to be needed for iOS 6 which doesn't seem to keep
    // label preferredMaxLayoutWidth in sync with its width, which 
    // means the label won't grow vertically to encompass its text if 
    // the label's width constraint changes.
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

@end
Monte Hurd
quelle
4

Ich denke, ich sollte einen Beitrag leisten, da ich eine Weile gebraucht habe, um die richtige Lösung zu finden:

  • Das Ziel ist es, Auto Layout seine Arbeit machen zu lassen, ohne jemals sizeToFit () aufzurufen. Dazu geben wir die richtigen Einschränkungen an:
  • Geben Sie für Ihr UILabel Einschränkungen für den oberen, unteren und führenden / nachfolgenden Speicherplatz an
  • Setzen Sie die Eigenschaft für die Anzahl der Zeilen auf 0
  • Erhöhen Sie die Priorität für das Umarmen von Inhalten auf 1000
  • Verringern Sie die Priorität des Inhaltskomprimierungswiderstands auf 500
  • Verringern Sie in Ihrer unteren Containereinschränkung die Priorität auf 500

Grundsätzlich sagen Sie Ihrem UILabel, dass es trotz einer festen Höhenbeschränkung die Einschränkung aufheben kann, um sich selbst zu verkleinern, um den Inhalt zu umarmen (wenn Sie beispielsweise eine einzelne Zeile haben), dies jedoch nicht kann Brechen Sie die Einschränkung, um sie größer zu machen.

ArturT
quelle
3

In meinem Fall habe ich eine UIView-Unterklasse erstellt, die ein UILabel (unbekannter Länge) enthielt. In iOS7 war der Code unkompliziert: Legen Sie die Einschränkungen fest, sorgen Sie sich nicht um das Umarmen von Inhalten oder die Komprimierungsbeständigkeit, und alles funktionierte wie erwartet.

In iOS6 wurde das UILabel jedoch immer auf eine einzelne Zeile beschränkt. Keine der obigen Antworten hat bei mir funktioniert. Einstellungen für Inhaltsumarmung und Kompressionswiderstand wurden ignoriert. Die einzige Lösung, die ein Abschneiden verhinderte, bestand darin, eine bevorzugte MaxLayoutWidth auf dem Etikett anzubringen. Ich wusste jedoch nicht, auf welche Einstellung ich die bevorzugte Breite einstellen sollte, da die Größe der übergeordneten Ansicht unbekannt war (tatsächlich würde sie durch den Inhalt definiert).

Hier habe ich endlich die Lösung gefunden . Da ich an einer benutzerdefinierten Ansicht arbeitete, konnte ich einfach die folgende Methode hinzufügen, um die bevorzugte Layoutbreite festzulegen, nachdem die Einschränkungen einmal berechnet wurden, und sie dann neu berechnen:

- (void)layoutSubviews
{
    // Autolayout hack required for iOS6
    [super layoutSubviews];
    self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
    [super layoutSubviews];
}
Sumizom
quelle
Dies war genau das Szenario, das ich hatte ... manchmal ist es schön zu wissen, dass Sie nicht allein sind.
DaveMac
Adam, ich kämpfe mit dem, was du gesagt hast, hat in ios7 perfekt funktioniert. Ich habe eine benutzerdefinierte Zelle mit einem uiview und einem uilabel. Ich kann das Etikett an den Inhalt anpassen, die Ansicht jedoch nicht, unabhängig davon, welche Einschränkungen ich auferlegt habe. Wie haben Sie es zum Laufen gebracht? Ich schwöre, ich habe alles versucht, aber es nimmt nicht die Höhe des Etiketts
denikov
3

Ich fügte UILabelprogrammatisch hinzu und in meinem Fall war das genug:

label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0
mischen
quelle
2

Ich habe mit xCode6 gelöst, indem ich "Bevorzugte Breite" auf "Automatisch" gesetzt und das Etikett oben, vorne und hinten angeheftet habe Geben Sie hier die Bildbeschreibung ein

Kazzar
quelle
0
UIFont *customFont = myLabel.font;
CGSize size = [trackerStr sizeWithFont:customFont
                             constrainedToSize:myLabel.frame.size // the size here should be the maximum size you want give to the label
                                 lineBreakMode:UILineBreakModeWordWrap];
float numberOfLines = size.height / customFont.lineHeight;
myLabel.numberOfLines = numberOfLines;
myLabel.frame = CGRectMake(258, 18, 224, (numberOfLines * customFont.lineHeight));
Swati
quelle
0

Ich bin auch auf dieses Problem mit einer UIView-Unterklasse gestoßen, die ein UILabel als eines seiner internen Elemente enthält. Selbst beim automatischen Auslegen und Ausprobieren aller empfohlenen Lösungen würde das Etikett seine Höhe nicht an den Text anpassen. In meinem Fall möchte ich immer nur, dass das Etikett eine Zeile enthält.

Für mich hat es jedenfalls funktioniert, eine erforderliche Höhenbeschränkung für das UILabel hinzuzufügen und diese manuell auf die richtige Höhe einzustellen, wenn intrinsicContentSize aufgerufen wird. Wenn Sie das UILabel nicht in einem anderen UIView enthalten haben, können Sie versuchen, UILabel in Unterklassen zu unterteilen und eine ähnliche Implementierung bereitzustellen, indem Sie zuerst die Höhenbeschränkung
festlegen und dann [super instrinsicContentSize] zurückgeben. anstelle von [self.containerview intrinsiceContentSize]; wie ich es unten tue, was spezifisch für meine UIView-Unterschicht ist.

- (CGSize)intrinsicContentSize
{
    CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
    self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
    return [self.containerView intrinsicContentSize];

}

Funktioniert jetzt perfekt unter iOS 7 und iOS 8.

n8tr
quelle
Es ist erwähnenswert, dass Sie niemals überschreiben müssen, wenn Sie Ihre Ansicht korrekt eingerichtet haben (dh Ihre Einschränkungen sind korrekt und Sie haben alles in den richtigen Phasen im Lebenszyklus der Ansicht eingerichtet) intrinsicContentSize. Die Laufzeit sollte dies basierend auf Ihren korrekt eingerichteten Einschränkungen berechnen können.
John Rogers
Theoretisch sollte das richtig sein, aber wir haben nicht Zugriff auf alle privaten APIs und Implementierungen von Apple und sie sind nicht unfehlbar und haben Fehler in ihrem Code.
n8tr
... aber [super intrinsicContentSize] sollte sich darum kümmern, sage ich. Wenn Sie Ihr automatisches Layout korrekt implementiert haben.
John Rogers
0

Eine Lösung, die für mich funktioniert hat; Wenn Ihr UILabel eine feste Breite hat, ändern Sie die Einschränkung in Ihrer Schnittstellendatei von constant =bisconstant <=

Geben Sie hier die Bildbeschreibung ein

Alex Brown
quelle
-1

In meinem Fall würde bei Verwendung der Beschriftungen in einer UITableViewCell die Größe der Beschriftung bei geändert, aber die Höhe würde die Höhe der Tabellenzellen überschreiten. Das hat bei mir funktioniert. Ich habe es laut Max MacLeod getan und dann sichergestellt, dass die Zellenhöhe auf UITableViewAutomaticDimension eingestellt ist.

Sie können dies in Ihrem init oder awakeFromNib hinzufügen.

self.tableView.rowHeight = UITableViewAutomaticDimension.  

Wählen Sie im Storyboard die Zelle aus, öffnen Sie den Größeninspektor und stellen Sie sicher, dass die Zeilenhöhe auf "Standard" eingestellt ist, indem Sie das Kontrollkästchen "Benutzerdefiniert" aktivieren.

Es gibt ein Problem in 8.0, bei dem es auch erforderlich ist, dass es im Code festgelegt wird.

Maria
quelle