Vraag Auto-lay-out gebruiken in UITableView voor dynamische cellay-outs en variabele rijhoogten


Hoe gebruik je Auto Layout binnen UITableViewCells in een tabelweergave om de inhoud en subvensters van elke cel de rijhoogte (zelf / automatisch) te laten bepalen, terwijl de vloeiende scrollprestaties behouden blijven?


1350
2017-09-11 16:48


oorsprong


antwoorden:


TL; DR: Hou je niet van lezen? Spring rechtstreeks naar de voorbeeldprojecten op GitHub:

Conceptuele beschrijving

De eerste 2 onderstaande stappen zijn van toepassing, ongeacht voor welke iOS-versies u zich ontwikkelt.

1. Beperkingen instellen en toevoegen

In uw UITableViewCell subklasse, voeg beperkingen toe zodat in de subbeelden van de cel de randen aan de randen van de cel worden vastgemaakt contentView (vooral naar de bovenste EN onderste randen). OPMERKING: pin geen subvisies op de cel zelf; alleen voor de cellen contentView! Laat de inhoud van de intrinsieke inhoud van deze subweergaven de hoogte van de inhoudsweergave van de tabelweergave bepalen door ervoor te zorgen dat de inhoud compressieweerstand en inhoud knuffelen beperkingen in de verticale dimensie voor elke subweergave worden niet overschreven door beperkingen met een hogere prioriteit die u hebt toegevoegd. (Huh? Klik hier.)

Houd er rekening mee dat het de bedoeling is om de subviews van de cel verticaal te laten aansluiten op de inhoudsweergave van de cel, zodat deze 'druk kan uitoefenen' en de inhoudweergave kan worden uitgebreid zodat ze passen. Met behulp van een voorbeeldcel met een paar subweergaven, hier is een visuele illustratie van wat sommige  (Niet alles!) van uw beperkingen zou er als volgt uit moeten zien:

Example illustration of constraints on a table view cell.

Je kunt je voorstellen dat naarmate meer tekst wordt toegevoegd aan het meerstammige bodylabel in de bovenstaande voorbeeldcel, deze verticaal moet groeien om in de tekst te passen, waardoor de cel effectief in de hoogte zal groeien. (Natuurlijk moet u de beperkingen goed krijgen om ervoor te zorgen dat dit correct werkt!)

Het is absoluut de moeite om je beperkingen goed te krijgen moeilijkste en belangrijkste deel van het krijgen van dynamische celhoogtes die werken met Auto Layout. Als je hier een fout maakt, kan dit voorkomen dat al het andere werkt - dus neem je tijd! Ik raad aan om uw beperkingen in code in te stellen, omdat u precies weet aan welke beperkingen wordt toegevoegd, en het is een stuk eenvoudiger om fouten te debuggen. Het toevoegen van beperkingen in code kan net zo eenvoudig en aanzienlijk krachtiger zijn dan Interface Builder met behulp van lay-outankers, of een van de fantastische open source API's die beschikbaar zijn op GitHub.

  • Als u beperkingen in code toevoegt, moet u dit een keer doen vanuit de updateConstraints methode van uw UITableViewCell-subklasse. Let daar op updateConstraints kan meerdere keren worden aangeroepen, dus om te voorkomen dat dezelfde beperkingen meer dan eens worden toegevoegd, moet u uw constraint-toevoegende code binnen updateConstraints in een controle voor een Boolean-eigenschap zoals didSetupConstraints (die u instelt op YES nadat u eenmaal uw constraint-toevoegcode hebt uitgevoerd). Aan de andere kant, als u code hebt die bestaande beperkingen bijwerkt (zoals het aanpassen van de constanteigenschap op enkele beperkingen), plaats dit in updateConstraints maar buiten de controle voor didSetupConstraints zodat het elke keer dat de methode wordt aangeroepen kan worden uitgevoerd.

2. Bepaal unieke tabelweergave voor celhergebruik

Voor elke unieke reeks beperkingen in de cel gebruikt u een uniek hergebruikskenmerk voor cellen. Met andere woorden, als uw cellen meer dan één unieke lay-out hebben, moet elke unieke lay-out een eigen hergebruik-ID krijgen. (Een goede hint dat u een nieuw ID voor hergebruik moet gebruiken is wanneer uw celvariant een ander aantal subweergaven heeft, of de subweergaven op een verschillende manier zijn gerangschikt.)

Als u bijvoorbeeld in elke cel een e-mailbericht weergeeft, hebt u mogelijk 4 unieke indelingen: berichten met alleen een onderwerp, berichten met een onderwerp en een hoofdtekst, berichten met een onderwerp en een fotobijlage en berichten met een onderwerp, hoofdgedeelte en fotobijlage. Elke lay-out heeft volledig verschillende beperkingen om deze te bereiken, dus zodra de cel is geïnitialiseerd en de beperkingen zijn toegevoegd voor een van deze celtypen, moet de cel een uniek hergebruiksherkenningstype krijgen dat specifiek is voor dat celtype. Dit betekent dat wanneer u een cel uit de cel haalt voor hergebruik, de beperkingen al zijn toegevoegd en klaar zijn om voor dat celtype te gaan.

Merk op dat als gevolg van verschillen in intrinsieke inhoudsgrootte, cellen met dezelfde beperkingen (type) mogelijk nog steeds verschillende hoogtes hebben! Verwar fundamenteel verschillende lay-outs (verschillende beperkingen) met verschillende berekende weergaveframes (opgelost vanuit identieke beperkingen) vanwege verschillende inhoudsgrootten.

  • Voeg geen cellen met volledig verschillende sets voorwaarden toe aan dezelfde hergebruikspool (dat wil zeggen, gebruik dezelfde hergebruiksherkenning) en probeer vervolgens de oude beperkingen te verwijderen en nieuwe beperkingen op te zetten na elke wachtrij. De interne Auto Layout-engine is niet ontworpen om grootschalige wijzigingen in beperkingen aan te kunnen, en u zult grote prestatieproblemen zien.

Voor iOS 8 - Self-Sizing Cells

3. Schathoogteberekening inschakelen

Om tabelcellen met zelftaken in te schakelen, moet u de tabelweergave instellen   rowHeight-eigenschap naar UITableViewAutomaticDimension. Je moet ook   een waarde toewijzen aan de geschatte eigenschap RowHeight. Zodra beide   deze eigenschappen worden ingesteld, het systeem maakt gebruik van Auto Layout om het te berekenen   rij werkelijke hoogte

Appel: Werken met zelf-bemeten tabelweergave Cellen

Met iOS 8 heeft Apple veel van het werk geïncorporeerd dat eerder door iOS 8 moest worden geïmplementeerd. Om het zelfregelende celmechanisme te laten werken, moet je eerst de rowHeight eigenschap op de tafelweergave voor de constante UITableViewAutomaticDimension. Vervolgens hoeft u alleen maar rijhoogte-schatting in te schakelen door de tabelweergave in te stellen estimatedRowHeight eigenschap tot een niet-nulwaarde, bijvoorbeeld:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is

Wat dit doet, is het tabeloverzicht voorzien van een tijdelijke schatting / tijdelijke aanduiding voor de rijhoogten van cellen die nog niet op het scherm staan. Wanneer deze cellen vervolgens op het scherm verschijnen, wordt de werkelijke rijhoogte berekend. Om de werkelijke hoogte voor elke rij te bepalen, wordt in de tabelweergave automatisch aan elke cel gevraagd welke hoogte die is contentViewmoet gebaseerd zijn op de bekende vaste breedte van de inhoudsweergave (die gebaseerd is op de breedte van de tabelweergave, minus eventuele aanvullende zaken zoals een sectie-index of accessoire-weergave) en de automatische lay-outbeperkingen die u hebt toegevoegd aan de contentweergave en subviews van de cel . Nadat deze werkelijke celhoogte is bepaald, wordt de oude geschatte hoogte voor de rij bijgewerkt met de nieuwe werkelijke hoogte (en eventuele aanpassingen aan de inhoud van de tabelweergave / inhoudOffset worden naar wens voor u gemaakt).

Over het algemeen hoeft de door u verstrekte schatting niet erg nauwkeurig te zijn - deze wordt alleen gebruikt om de schuifindicator in de tabelweergave correct te maken en de tabelweergave kan de schuifindicator goed aanpassen aan onjuiste schattingen als u scrol cellen op het scherm. U moet de estimatedRowHeight eigenschap op de tafelweergave (in viewDidLoad of vergelijkbaar) naar een constante waarde die de "gemiddelde" rijhoogte is. Alleen als uw rijhoogten extreem variabel zijn (bijvoorbeeld verschillen in grootteorde) en u de scroll-indicator "springen" ziet terwijl u bladert, moet u de implementatie ervan proberen tableView:estimatedHeightForRowAtIndexPath: om de minimale berekening te doen die vereist is om een ​​meer nauwkeurige schatting voor elke rij te retourneren.

Voor iOS 7-ondersteuning (zelfautomatisering met automatische cellen implementeren)

3. Voer een lay-outpas uit en haal de celhoogte op

Eerst een instant-instantie van een tabelweergavecel instantiëren één instantie voor elke hergebruik-ID, dat alleen voor hoogteberekeningen wordt gebruikt. (Offscreen betekent dat de celverwijzing is opgeslagen in een eigenschap / ivar op de view controller en nooit is geretourneerd tableView:cellForRowAtIndexPath: voor de tabelweergave om daadwerkelijk op het scherm weer te geven.) Vervolgens moet de cel worden geconfigureerd met de exacte inhoud (bijvoorbeeld tekst, afbeeldingen, enz.) die deze zou bevatten als deze zou worden weergegeven in de tabelweergave.

Forceer vervolgens de cel om zijn subweergaven onmiddellijk in te delen en gebruik vervolgens de systemLayoutSizeFittingSize: methode op de UITableViewCell's contentView om erachter te komen wat de vereiste hoogte van de cel is. Gebruik UILayoutFittingCompressedSize om de kleinste maat te krijgen die voor alle inhoud van de cel nodig is. De hoogte kan vervolgens worden geretourneerd uit de tableView:heightForRowAtIndexPath: gedelegeerde methode.

4. Gebruik geschatte rijhoogtes

Als uw tabelweergave meer dan een paar dozijn rijen bevat, zult u merken dat het oplossen van de Auto-layout-beperking snel de hoofddraad kan wegvagen bij het laden van de tabelweergave, zoals tableView:heightForRowAtIndexPath: wordt bij elke eerste lading op elke rij aangeroepen (om de grootte van de schuifindicator te berekenen).

Vanaf iOS 7 kun je ( estimatedRowHeight eigendom op het tafelblad. Wat dit doet, is het tabeloverzicht voorzien van een tijdelijke schatting / tijdelijke aanduiding voor de rijhoogten van cellen die nog niet op het scherm staan. Wanneer deze cellen vervolgens op het scherm verschijnen, wordt de werkelijke rijhoogte berekend (door te bellen tableView:heightForRowAtIndexPath:) en de geschatte hoogte bijgewerkt met de werkelijke.

Over het algemeen hoeft de door u verstrekte schatting niet erg nauwkeurig te zijn - deze wordt alleen gebruikt om de schuifindicator in de tabelweergave correct te maken en de tabelweergave kan de schuifindicator goed aanpassen aan onjuiste schattingen als u scrol cellen op het scherm. U moet de estimatedRowHeight eigenschap op de tafelweergave (in viewDidLoad of vergelijkbaar) naar een constante waarde die de "gemiddelde" rijhoogte is. Alleen als uw rijhoogten extreem variabel zijn (bijvoorbeeld verschillen in grootteorde) en u de scroll-indicator "springen" ziet terwijl u bladert, moet u de implementatie ervan proberen tableView:estimatedHeightForRowAtIndexPath: om de minimale berekening te doen die vereist is om een ​​meer nauwkeurige schatting voor elke rij te retourneren.

5. (indien nodig) Rijhoogte-caching toevoegen

Als je al het bovenstaande hebt gedaan en nog steeds merkt dat de prestaties onaanvaardbaar traag zijn bij het oplossen van de beperking tableView:heightForRowAtIndexPath:, je zult helaas wat caching voor celhoogtes moeten implementeren. (Dit is de benadering die wordt voorgesteld door de technici van Apple.) Het algemene idee is om de Auto Layout-engine de beperkingen de eerste keer te laten oplossen, vervolgens de berekende hoogte voor die cel in cache op te slaan en de gecachte waarde te gebruiken voor alle toekomstige verzoeken voor de hoogte van die cel. De truc is natuurlijk om ervoor te zorgen dat je de gecachetiseerde hoogte voor een cel opruimt wanneer er iets gebeurt waardoor de hoogte van de cel kan veranderen - dit zou in de eerste plaats het geval zijn wanneer de inhoud van die cel verandert of wanneer zich andere belangrijke gebeurtenissen voordoen (zoals het aanpassen door de gebruiker). de schuifregelaar Dynamiektype-tekstgrootte).

iOS 7 Generieke voorbeeldcode (met veel sappige opmerkingen)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path, depending on the particular layout required (you may have
    // just one, or may have many).
    NSString *reuseIdentifier = ...;

    // Dequeue a cell for the reuse identifier.
    // Note that this method will init and return a new cell if there isn't
    // one available in the reuse pool, so either way after this line of 
    // code you will have a cell with the correct constraints ready to go.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // If you are using multi-line UILabels, don't forget that the 
    // preferredMaxLayoutWidth needs to be set correctly. Do it at this 
    // point if you are NOT doing it within the UITableViewCell subclass 
    // -[layoutSubviews] method. For example: 
    // cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path.
    NSString *reuseIdentifier = ...;

    // Use a dictionary of offscreen cells to get a cell for the reuse 
    // identifier, creating a cell and storing it in the dictionary if one 
    // hasn't already been added for the reuse identifier. WARNING: Don't 
    // call the table view's dequeueReusableCellWithIdentifier: method here 
    // because this will result in a memory leak as the cell is created but 
    // never returned from the tableView:cellForRowAtIndexPath: method!
    UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
    if (!cell) {
        cell = [[YourTableViewCellClass alloc] init];
        [self.offscreenCells setObject:cell forKey:reuseIdentifier];
    }

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // Set the width of the cell to match the width of the table view. This
    // is important so that we'll get the correct cell height for different
    // table view widths if the cell's height depends on its width (due to 
    // multi-line UILabels word wrapping, etc). We don't need to do this 
    // above in -[tableView:cellForRowAtIndexPath] because it happens 
    // automatically when the cell is used in the table view. Also note, 
    // the final width of the cell may not be the width of the table view in
    // some cases, for example when a section index is displayed along 
    // the right side of the table view. You must account for the reduced 
    // cell width.
    cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));

    // Do the layout pass on the cell, which will calculate the frames for 
    // all the views based on the constraints. (Note that you must set the 
    // preferredMaxLayoutWidth on multi-line UILabels inside the 
    // -[layoutSubviews] method of the UITableViewCell subclass, or do it 
    // manually at this point before the below 2 lines!)
    [cell setNeedsLayout];
    [cell layoutIfNeeded];

    // Get the actual height required for the cell's contentView
    CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    // Add an extra point to the height to account for the cell separator, 
    // which is added between the bottom of the cell's contentView and the 
    // bottom of the table view cell.
    height += 1.0;

    return height;
}

// NOTE: Set the table view's estimatedRowHeight property instead of 
// implementing the below method, UNLESS you have extreme variability in 
// your row heights and you notice the scroll indicator "jumping" 
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Do the minimal calculations required to be able to return an 
    // estimated row height that's within an order of magnitude of the 
    // actual height. For example:
    if ([self isTallCellAtIndexPath:indexPath]) {
        return 350.0;
    } else {
        return 40.0;
    }
}

Voorbeeldprojecten

Deze projecten zijn volledig werkende voorbeelden van tabelweergaven met variabele rijhoogten vanwege tabelweergavecellen met dynamische inhoud in UILabels.

Xamarin (C # / .NET)

Als u Xamarin gebruikt, bekijk dit dan voorbeeldproject samengesteld door @KentBoogaart.


2265
2018-06-19 08:21



Voor IOS8 is het heel eenvoudig:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableViewAutomaticDimension
}

OF

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

Maar voor IOS7 is de sleutel het berekenen van de hoogte na autolayout,

func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
    return height
}

Belangrijk 

  • Als er meerdere regels zijn, vergeet dan niet het numberOfLines naar 0.

  • Vergeet niet label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

De volledige voorbeeldcode is hier.


122
2018-04-05 09:23



Snel voorbeeld van UITableViewCell met variabele hoogte

Bijgewerkt voor Swift 3

Het snelle antwoord van William Hu is goed, maar het helpt me om een ​​paar eenvoudige maar gedetailleerde stappen te zetten als ik iets voor de eerste keer leer. Het onderstaande voorbeeld is mijn testproject tijdens het leren maken van een UITableView met variabele celhoogten. Ik baseerde het op dit eenvoudige UITableView-voorbeeld voor Swift.

Het voltooide project zou er als volgt uit moeten zien:

enter image description here

Maak een nieuw project

Het kan gewoon een toepassing voor één weergave zijn.

Voeg de code toe

Voeg een nieuw Swift-bestand toe aan uw project. Noem het MyCustomCell. Deze klasse bevat de verkooppunten voor de weergaven die je toevoegt aan je cel op het storyboard. In dit basisvoorbeeld hebben we slechts één label in elke cel.

import UIKit
class MyCustomCell: UITableViewCell {
    @IBOutlet weak var myCellLabel: UILabel!
}

We zullen dit stopcontact later verbinden.

Open ViewController.swift en zorg dat je de volgende inhoud hebt:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    let animals: [String] = [
        "Ten horses:  horse horse horse horse horse horse horse horse horse horse ",
        "Three cows:  cow, cow, cow",
        "One camel:  camel",
        "Ninety-nine sheep:  sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
        "Thirty goats:  goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]

    // Don't forget to enter this in IB also
    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // delegate and data source
        tableView.delegate = self
        tableView.dataSource = self

        // Along with auto layout, these are the keys for enabling variable cell height
        tableView.estimatedRowHeight = 44.0
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
        cell.myCellLabel.text = self.animals[indexPath.row]
        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }
}

Belangrijke notitie:

  • Het zijn de volgende twee coderegels (samen met de automatische lay-out) die de variabele celhoogte mogelijk maken:

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableViewAutomaticDimension
    

Stel het storyboard in

Voeg een tabelweergave toe aan uw weergavecontroller en gebruik de automatische lay-out om deze aan de vier zijden vast te pinnen. Sleep vervolgens een tabelweergavecel naar de tabelweergave. En sleep een label naar de cel Prototype. Gebruik de automatische lay-out om het label vast te pinnen aan de vier randen van de inhoudsweergave van de tabelweergavecel.

enter image description here

Belangrijke notitie:

  • Auto-indeling werkt samen met de twee belangrijke coderegels die ik hierboven heb genoemd. Als u de automatische lay-out niet gebruikt, werkt deze niet.

Andere IB-instellingen

Aangepaste klassennaam en identificatie

Selecteer de tabelweergavecel en stel de aangepaste klasse in MyCustomCell (de naam van de klas in het Swift-bestand dat we hebben toegevoegd). Stel ook de Identifier in cell(dezelfde reeks die we hebben gebruikt voor de cellReuseIdentifier in de bovenstaande code.

enter image description here

Nul regels voor label

Stel het aantal regels in 0 in je label. Dit betekent multi-regel en laat het label zelf verkleinen op basis van de inhoud.

enter image description here 

Sluit de stopcontacten aan

  • Regel het slepen vanuit de tabelweergave in het storyboard naar de tableView variabele in de ViewController code.
  • Doe hetzelfde voor het label in uw Prototype-cel voor het myCellLabel variabele in de MyCustomCell klasse.

Afgewerkt

Je zou nu je project moeten kunnen uitvoeren en cellen met variabele hoogtes krijgen.

Notes

  • Dit voorbeeld werkt alleen voor iOS 8 en later. Als je iOS 7 nog steeds moet ondersteunen, dan werkt dit niet voor jou.
  • Uw eigen aangepaste cellen in uw toekomstige projecten hebben waarschijnlijk meer dan één label. Zorg ervoor dat u alles goed vasthoudt zodat de automatische lay-out de juiste hoogte kan bepalen om te gebruiken. Mogelijk moet u ook verticale compressieweerstand en knuffelen gebruiken. Zien Dit artikel voor meer daarover.
  • Als u de voor- en achterrand (links en rechts) niet vastzet, moet u mogelijk ook de labels instellen preferredMaxLayoutWidth zodat hij weet wanneer hij moet afwikkelen. Als u bijvoorbeeld een Midden-horizontaal beperking aan het label in het bovenstaande project hebt toegevoegd in plaats van de voor- en achterranden vast te pinnen, moet u deze regel toevoegen aan de tableView:cellForRowAtIndexPath methode:

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
    

Zie ook


71
2018-06-11 15:18



Ik heb de iOS7-oplossing van smileyborg in een categorie ingepakt

Ik besloot om deze slimme oplossing van @smileyborg om te zetten in een UICollectionViewCell+AutoLayoutDynamicHeightCalculation categorie.

De categorie corrigeert ook de problemen die zijn beschreven in het antwoord van @ wildmonkey (een cel laden vanuit een punt en systemLayoutSizeFittingSize: terugkerende CGRectZero)

Er wordt geen rekening gehouden met caching maar past nu precies bij mijn behoeften. Je mag het kopiëren, plakken en hacken.

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.h

#import <UIKit/UIKit.h>

typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);

/**
 *  A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
 *
 *  Many thanks to @smileyborg and @wildmonkey
 *
 *  @see stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
 */
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)

/**
 *  Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
 *
 *  @param name Name of the nib file.
 *
 *  @return collection view cell for using to calculate content based height
 */
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;

/**
 *  Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
 *
 *  @param block Render the model data to your UI elements in this block
 *
 *  @return Calculated constraint derived height
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;

/**
 *  Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;

@end

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.m

#import "UICollectionViewCell+AutoLayout.h"

@implementation UICollectionViewCell (AutoLayout)

#pragma mark Dummy Cell Generator

+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
    UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
    [heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
    return heightCalculationCell;
}

#pragma mark Moving Constraints

- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        [self removeConstraint:constraint];
        id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
        id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
                                                                     attribute:constraint.firstAttribute
                                                                     relatedBy:constraint.relation
                                                                        toItem:secondItem
                                                                     attribute:constraint.secondAttribute
                                                                    multiplier:constraint.multiplier
                                                                      constant:constraint.constant]];
    }];
}

#pragma mark Height

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
    return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
                                            collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
    NSParameterAssert(block);

    block();

    [self setNeedsUpdateConstraints];
    [self updateConstraintsIfNeeded];

    self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));

    [self setNeedsLayout];
    [self layoutIfNeeded];

    CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    return calculatedSize.height;

}

@end

Gebruik voorbeeld:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
    CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
        [(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
    }];
    return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}

Gelukkig hoeven we deze jazz niet te doen in iOS8, maar daar is het nu voor!


60
2018-02-10 19:41



Hier is mijn oplossing. U moet de TableView geven de geschatte Hoogte voordat het de weergave laadt. Anders zal het zich niet kunnen gedragen zoals verwacht.

- (void)viewWillAppear:(BOOL)animated {
    _messageField.delegate = self;
    _tableView.estimatedRowHeight = 65.0;
    _tableView.rowHeight = UITableViewAutomaticDimension;
}

43
2017-11-11 16:52



De oplossing die wordt voorgesteld door @smileyborg is bijna perfect. Als u een aangepaste cel heeft en u een of meer wilt UILabel met dynamische hoogten dan de systemLayoutSizeFittingSize methode gecombineerd met AutoLayout ingeschakeld geeft a CGSizeZero tenzij je al je celbeperkingen vanuit de cel verplaatst naar zijn inhoudView (zoals voorgesteld door @TomSwift hier Hoe kan ik superview aanpassen zodat alle subviews passen met autolayout?).

Om dit te doen, moet je de volgende code invoegen in je aangepaste UITableViewCell-implementatie (met dank aan @Adrian).

- (void)awakeFromNib{
    [super awakeFromNib];
    for (NSLayoutConstraint *cellConstraint in self.constraints) {
        [self removeConstraint:cellConstraint];
        id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
        id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
        NSLayoutConstraint *contentViewConstraint =
        [NSLayoutConstraint constraintWithItem:firstItem
                                 attribute:cellConstraint.firstAttribute
                                 relatedBy:cellConstraint.relation
                                    toItem:seccondItem
                                 attribute:cellConstraint.secondAttribute
                                multiplier:cellConstraint.multiplier
                                  constant:cellConstraint.constant];
        [self.contentView addConstraint:contentViewConstraint];
    }
}

Het mengen van @smileyborg antwoord hiermee zou moeten werken.


42
2017-12-03 02:23



Een belangrijk genoeg die ik net tegenkwam om als antwoord te posten.

@ smileyborg's antwoord is grotendeels correct. Als u echter een code in de layoutSubviews methode van uw aangepaste celklasse, bijvoorbeeld het instellen van de preferredMaxLayoutWidth, dan zal het niet worden uitgevoerd met deze code:

[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];

Het heeft me een tijdje verward. Toen besefte ik dat het komt omdat die alleen lay-outSubviews triggeren op de contentView, niet de cel zelf.

Mijn werkende code ziet er zo uit:

TCAnswerDetailAppSummaryCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailAppSummaryCell"];
[cell configureWithThirdPartyObject:self.app];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;

Houd er rekening mee dat als u een nieuwe cel maakt, ik vrij zeker weet dat u niet hoeft te bellen setNeedsLayout zoals het al zou moeten zijn ingesteld. In gevallen waarin u een verwijzing naar een cel opslaat, zou u het waarschijnlijk moeten bellen. Hoe dan ook, het zou niets moeten doen.

Nog een tip als je cel-subklassen gebruikt waar je dingen in zet preferredMaxLayoutWidth. Zoals @smileyborg vermeldt, "uw tafelweergavecel heeft zijn breedte nog niet vastgezet aan de breedte van het tafelblad". Dit klopt, en problemen als je je werk doet in je subklasse en niet in de view controller. U kunt echter eenvoudig het celframe instellen met behulp van de tabelbreedte:

Bijvoorbeeld in de berekening van de hoogte:

self.summaryCell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailDefaultSummaryCell"];
CGRect oldFrame = self.summaryCell.frame;
self.summaryCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, self.tableView.frame.size.width, oldFrame.size.height);

(Ik bewaar deze specifieke cel toevallig voor hergebruik, maar dat is niet relevant).


22
2018-03-05 15:53