web-dev-qa-db-de.com

Mehrere UILabels innerhalb einer UITableViewCell mit eigener Größe

In dieser iOS 8-App, die ich erstelle, habe ich eine Tabellenansicht, und ich brauche sie zur Größenänderung. Ich habe es mit Auto Layout implementiert und es funktioniert. Fast. So sieht es jetzt aus.

enter image description here

Es gibt 3 Beschriftungen in einer Zelle. Hauptetikett mit dem Lorem ipsum-Text. Untertitel, der die Zahlenfolge enthält (Dies sind zwei separate Beschriftungen. Könnte verwirrend sein, weil sie dieselbe Farbe haben.) Dann die dritte Beschriftung mit dem kleinen schwarzen Text.

Das erste Etikett hat sich ohne Probleme korrekt skaliert und das zweite Etikett wird entsprechend auf und ab verschoben. Das Problem liegt jedoch beim dritten kleinen Label. Wie Sie sehen, ändert sich die Größe nicht, um den gesamten Text anzupassen.

Jetzt passiert etwas Seltsames. Ich drehe es um und hier ist es.

enter image description here

Da auf dem Etikett Platz ist, wird der gesamte Text angezeigt. Fein. Dann wende ich mich wieder dem Porträt zu.

enter image description here

Jetzt hat sich das kleine Etikett an seinen gesamten Text angepasst, überschreitet jedoch die Zellgrenzen. Ich habe versucht, die Zelle größer zu machen, aber es hat nicht funktioniert. Da es sich um Zellen zur Selbstanpassung von Zellen handelt, glaube ich nicht, dass dies sogar der richtige Weg ist.

Ich bekomme auch keine Fehler oder auch keine Warnung zu meinen automatischen Layout-Einschränkungen.

enter image description here

Ich habe diese beiden Codezeilen in der viewDidLoad()-Methode festgelegt.

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension

Kann mir bitte jemand sagen, was ich hier falsch machen könnte?

Da es schwierig ist, nur durch das Betrachten von Bildern zu antworten, und ich keinen weiteren Code neben dem obigen Snippet veröffentlichen kann, habe ich ein lauffähiges Xcode-Projekt hochgeladen, in dem das Problem hier dargestellt wird. (Es gibt 2 benutzerdefinierte Zellen. Im Grunde ist es die gleiche Zelle, nur die Höhe wird in der zweiten Zelle erhöht.)

Ich habe mit automatischen Layoutbeschränkungen herumgespielt, aber ich kann nicht scheinen, dass dies funktioniert. Jede Hilfe wäre dankbar.

Vielen Dank.


UPDATE:

Mit Hilfe dieses Tutorials habe ich einige hilfreiche Hinweise gefunden. Demnach sollte jede Unteransicht Einschränkungen haben, die alle Seiten fixieren, und es sollte Einschränkungen geben, die sich von oben nach unten erstrecken, was dem automatischen Layout zur Berechnung der Höhe der Zelle hilft. In meinem ursprünglichen Beitrag hatte ich vertikale Abstände zwischen den einzelnen Beschriftungen, daher glaube ich, dass das automatische Layout die korrekte Höhe nicht berechnen konnte.

Also habe ich einige Änderungen vorgenommen.

  • Ich habe den vertikalen Abstand zwischen Beschriftungen auf 0 reduziert und die Beschränkungen für den vertikalen Abstand zwischen oberen und mittleren Beschriftungen sowie mittleren und unteren Beschriftungen festgelegt.
  • Ich fügte der oberen Beschriftung führende, obere und nachfolgende Einschränkungen hinzu.
  • Führend und nachlaufend zum mittleren Etikett.
  • Führend, unten, hinter dem unteren Etikett.

Nun, hier ist ein weiterer komischer Teil. Wenn ich es zum ersten Mal starte, ist das Problem beim Beschneiden der unteren Etiketten immer noch vorhanden.

enter image description here

Wenn ich das Gerät jedoch im Querformat drehen und wieder im Hochformat drehen, werden alle Zellen so angepasst, dass sie für beide Beschriftungen passen!

enter image description here

Ich kann immer noch nicht herausfinden, warum das auf den ersten Blick nicht passiert. Das aktualisierte Xcode-Projekt ist hier .

57
Isuru

Hier geht es um die preferredMaxLayoutWidth-Eigenschaft der mehrzeiligen Beschriftungen. Dies ist die Eigenschaft, die der Beschriftung mitteilt, wann Word umbrochen werden soll. Sie muss korrekt eingestellt sein, damit die Variable _intrinsicContentSize jedes Labels die korrekte Höhe hat. Dies ist letztlich das, was das automatische Layout zur Bestimmung der Höhe der Zelle verwendet.

Mit dem Xcode 6 Interface Builder wurde eine neue Option eingeführt, um diese Eigenschaft auf Automatic zu setzen. Leider gibt es einige schwerwiegende Fehler (ab Xcode 6.2/iOS 8.2), bei denen dies beim Laden einer Zelle aus einer Feder oder einem Storyboard nicht richtig/automatisch eingestellt wird.

Um diesen Fehler zu umgehen, muss die Variable preferredMaxLayoutWidth genau der endgültigen Breite des Etiketts entsprechen, sobald es in der Tabellenansicht angezeigt wird. Wir möchten vor der Rückgabe der Zelle aus tableView:cellForRowAtIndexPath: Folgendes tun:

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)

Das alleinige Hinzufügen dieses Codes allein ist nicht geeignet, weil bei Ausführung dieser 3 Codezeilen in tableView:cellForRowAtIndexPath: die Breite der einzelnen Beschriftungen verwendet wird, um die preferredMaxLayoutWidth festzulegen. Wenn Sie jedoch die Breite der Beschriftungen hier überprüfen, wird die Variable verwendet Zu einem bestimmten Zeitpunkt unterscheidet sich die Etikettenbreite völlig von der, bei der die Zelle am Ende angezeigt wird und ihre Unteransichten angeordnet wurden.

Wie können wir erreichen, dass die Etikettenbreite an diesem Punkt genau ist, damit sie ihre endgültige Breite widerspiegeln? Hier ist der Code, durch den alles zusammenkommt:

// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell

cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)
cell.contentView.bounds = cell.bounds
cell.layoutIfNeeded()

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)

OK, was machen wir hier? Sie werden feststellen, dass 3 neue Codezeilen hinzugefügt wurden. Zuerst müssen wir die Breite dieser Tabellensichtzelle so einstellen, dass sie mit der tatsächlichen Breite der Tabellensicht übereinstimmt (dies setzt voraus, dass die Tabellensicht bereits ausgelegt wurde und ihre endgültige Breite hat, was der Fall sein sollte). Wir machen effektiv nur die Zellbreite frühzeitig korrigieren, da die Tabellenansicht dies letztendlich tun wird.

Sie werden auch feststellen, dass wir 99999 für die Höhe verwenden. Um was geht es? Dies ist eine einfache Problemumgehung für das im Einzelnen beschriebene Problem hier . Wenn für Ihre Einschränkungen mehr vertikaler Speicherplatz als die aktuelle Höhe der contentView der Zelle erforderlich ist, wird eine Ausnahmebedingungsausnahme angezeigt, die eigentlich kein wirkliches Problem anzeigt . Die Höhe der Zelle oder einer ihrer Unteransichten spielt an dieser Stelle keine Rolle, da uns nur die endgültigen Breiten für jedes Etikett wichtig sind.

Als Nächstes stellen wir sicher, dass die contentView der Zelle dieselbe Größe hat, wie wir sie gerade der Zelle selbst zugewiesen haben, indem wir die Grenzen der contentView so setzen, dass sie den Grenzen der Zelle entsprechen. Dies ist erforderlich, da alle von Ihnen erstellten Einschränkungen für das automatische Layout relativ zur contentView sind. Daher muss die contentView die richtige Größe haben, damit sie korrekt gelöst werden kann. Wenn Sie die Größe der Zelle manuell einstellen, wird nicht die contentView automatisch an die Größe anpassen.

Schließlich erzwingen wir einen Layout-Durchlauf für die Zelle, wodurch die Auto-Layout-Engine Ihre Einschränkungen lösen und die Frames aller Unteransichten aktualisieren muss. Da Zelle und contentView jetzt die gleiche Breite haben, wie sie zur Laufzeit in der Tabellensicht angezeigt wird, sind auch die Etikettenbreiten korrekt. Dies bedeutet, dass die für jedes Etikett festgelegte Variable preferredMaxLayoutWidth genau ist und dazu führt, dass das Etikett zum richtigen Zeitpunkt umgebrochen wird Dies bedeutet natürlich, dass die Höhen der Beschriftungen korrekt festgelegt werden, wenn die Zelle in der Tabellenansicht verwendet wird!

Dies ist definitiv ein Apple-Fehler in UIKit, den wir vorläufig umgehen müssen (machen Sie daher bitte Datei-Fehlerberichte mit Apple, damit sie einen Fix priorisieren!).

Eine letzte Anmerkung: Diese Problemumgehung wird in Schwierigkeiten geraten, wenn die Breite der Tabellenansicht-Zelle contentView nicht die volle Breite der Tabellenansicht erweitert, beispielsweise wenn rechts ein Abschnittsindex angezeigt wird. In diesem Fall müssen Sie sicherstellen, dass Sie dies beim Einstellen der Breite der Zelle manuell berücksichtigen - Sie müssen diese Werte möglicherweise hartcodieren, etwa:

let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)
81
smileyborg

Ich habe das gleiche Problem wie Sie getroffen und eine einfache Lösung gefunden, um das Problem zu lösen.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     // dequeue cell...

     // do autolayout staffs...or if the autolayout rule has been set in xib, do nothing

     [cell layoutIfNeeded];

     return cell;
}

Und das Self-Sizing hat gut funktioniert. In meinem Code habe ich zwei Etiketten in vertikaler Richtung gelegt, beide haben eine dynamische Höhe. Die Höhe der Zelle ist korrekt so eingestellt, dass sie die beiden Beschriftungen enthält. 

49
fogisland

Angenommen, Sie haben keine Fehler mit Ihren Einschränkungen, wie von anderen vorgeschlagen, scheint dieses Problem auf die Verwendung eines UILabels zurückzuführen zu sein, das mehrere Zeilen in Verbindung mit einem UITableViewCellAccessory zulässt. Wenn iOS die Zelle auslegt und die Höhe bestimmt, berücksichtigt sie nicht die Versatzänderung in der Breite, die aufgrund dieses Zubehörs auftritt, und Sie werden abgeschnitten, wo Sie dies nicht erwarten würden. 

Angenommen, Sie möchten, dass das UILabel die gesamte Breite der Inhaltsansicht erweitert, schrieb ich eine Methode, mit der dies für alle Schriftgrößen korrigiert wird

-(void)fixWidth:(UILabel *)label forCell:(UITableViewCell *)cell {
    float offset = 0;
    switch ([cell accessoryType]) {
        case UITableViewCellAccessoryCheckmark:
            offset = 39.0;
            break;
        case UITableViewCellAccessoryDetailButton:
            offset = 47.0;
            break;
        case UITableViewCellAccessoryDetailDisclosureButton:
            offset = 67.0;
            break;
        case UITableViewCellAccessoryDisclosureIndicator:
            offset = 33.0;
            break;
        case UITableViewCellAccessoryNone:
            offset = 0;
            break;
    }
    [label setPreferredMaxLayoutWidth:CGRectGetWidth([[self tableView]frame]) - offset - 8];
}

Fügen Sie dies einfach in Ihren cellForRowAtIndexPath ein

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    #Setup the cell
    ...
    // Fix layout with accessory view
    [self fixWidth:[cell label] forCell:cell];
    return cell;
}

Tun Sie dies für alle Etiketten, die mehrere Zeilen haben, um die Breite richtig anzupassen, und berechnen Sie dann die entsprechenden Höhen neu. Dies funktioniert auch bei dynamischen Schriftgrößen.

Wie von smileyborg erwähnt, könnten Sie, wenn Sie nicht die gesamte Breite des contentView verwenden, die Einschränkungen referenzieren und sie auch von der Breite abziehen.

Edit: Ich habe vorher 'layoutIfNeeded' in der Zelle ausgeführt, aber dies führte zu Leistungsproblemen und schien ohnehin nicht erforderlich zu sein. Das Entfernen hat mir keine Probleme bereitet.

6
user2898617

Sie haben hier zwei Probleme.

1/Im Moment können Sie mit Visual Format Language die vertikalen Einschränkungen Ihrer Zelle folgendermaßen übersetzen:

Cell: "V:|-(10)-[nameLabel]-(67)-|"

Dann legen Sie eine zweite Gruppe von Einschränkungen fest:

Cell: "V:|-(10)-[nameLabel]-(8)-[pnrLabel]-(2)-[actionsLabel]"

Diese beiden Gruppen von Einschränkungen können sich nicht gut mischen und zeigen ihre Zweideutigkeit mit Ihrem zweiten Problem.

2/Aus bestimmten Gründen ist actionsLabel beim Start Ihrer App auf eine Zeile begrenzt. Wenn Sie Ihr Gerät in den Querformatmodus drehen, akzeptiert actionsLabel die Anzeige mit zwei oder mehr Zeilen. Wenn Sie Ihr Gerät dann wieder in den Hochformatmodus drehen, zeigt actionsLabel zwei oder mehr Zeilen an. Da actionsLabel jedoch nicht wirklich zu den Höhenbeschränkungen Ihrer Zelle gehört, überlappen sie die Grenzen Ihrer Zelle.

Wenn Sie alle diese Probleme lösen möchten, empfehle ich zunächst, dass Sie Ihre Xib-Datei von Grund auf neu erstellen. Dies heilt Ihr actionsLabel seltsames Verhalten (zwei Zeilen oder mehr nur, wenn Sie Ihr Gerät drehen).

Dann müssen Sie Ihre Einschränkungen so definieren:

Cell: "V:|-(10)-[nameLabel(>=21)]-(8)-[pnrLabel(>=21)]-(2)-[actionsLabel(>=21)]-(10)-|"

Natürlich können Sie für Ihre Beschriftungen andere Mindesthöheneinschränkungen als (>=21) definieren. Auf dieselbe Weise kann der untere Rand auf einen anderen Wert als -(10)- eingestellt werden.

Nachtrag

Um Ihre Frage zu beantworten, habe ich ein einfaches Projekt mit dem vorherigen Einschränkungsmuster in meiner .xib-Datei erstellt. Das folgende Bild kann Ihnen dabei helfen, Ihre eigenen Einschränkungen zu erstellen.

enter image description here

4
Imanou Petit

Ich habe die sehr einfache und elegant aussehende Lösung von "fogisland" ausprobiert - und es hat nicht funktioniert. Glücklicherweise habe ich herausgefunden, dass eine zusätzliche Zeile es in alle Richtungen funktionieren lässt. Sagen Sie dem System einfach, dass Sie nicht nur ein neues Layout vorschlagen (layoutIfNeeded), sondern explizit danach fragen (setNeedLayout).

    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    return cell
1
Hardy_Germany