Używanie układu automatycznego w UITableView dla dynamicznych układów komórek i zmiennych wysokości wierszy

Jak używać automatycznego układu w obrębie UITableViewCells W widoku tabeli, aby zawartość każdej komórki i podglądy określały wysokość wiersza( sam/automatycznie), przy zachowaniu wydajności płynnego przewijania?

Author: Krunal, 2013-09-11

25 answers

TL; DR: nie lubisz czytać? Przejdź od razu do przykładowych projektów na Githubie: [34]}

  • IOS 8 przykładowy projekt - wymaga iOS 8
  • W 2009 roku firma została założona przez firmę iOS.]}

Opis Koncepcyjny

Pierwsze 2 poniższe kroki mają zastosowanie niezależnie od wersji systemu iOS, dla których tworzysz.

1. Konfiguracja I Dodawanie Ograniczeń

W podklasie UITableViewCell Dodaj ograniczenia tak, aby podglądy komórki mają swoje krawędzie przypięte do krawędzi contentView (przede wszystkim do górnej i dolnej krawędzi). uwaga: nie przypinaj podviewów do samej komórki; tylko do komórki contentView! niech Wewnętrzny rozmiar zawartości tych podwidywań steruje wysokością widoku zawartości komórki widoku tabeli, upewniając się, że ograniczenia odporność na kompresję zawartości i content hugging w wymiarze pionowym dla każdego podwidywacza nie są przesłonięte przez dodano ograniczenia o wyższym priorytecie. (Huh? Kliknij tutaj.)

Pamiętaj, że chodzi o to, aby podwinięcia komórki były połączone pionowo z widokiem zawartości komórki, aby mogły "wywierać nacisk" i aby Widok zawartości rozszerzał się, aby pasował do nich. Korzystając z przykładowej komórki z kilkoma podwinięciami, oto wizualna ilustracja tego, co niektóre (nie wszyscy!) Twoje ograniczenia musiałyby wyglądać następująco:

Przykładowa ilustracja ograniczeń w komórce widoku tabeli.

Można sobie wyobrazić, że jako więcej tekstu jest dodawany do wielowierszowej etykiety ciała w przykładowej komórce powyżej, będzie musiał rosnąć pionowo, aby dopasować tekst, co skutecznie wymusi wzrost komórki na wysokości. (Oczywiście, musisz uzyskać odpowiednie ograniczenia, aby to działało poprawnie!)

Poprawienie ograniczeń jest zdecydowanie najtrudniejszą i najważniejszą częścią uzyskiwania dynamicznych wysokości komórek pracujących z automatycznym układem. Jeśli popełnisz błąd tutaj, może to uniemożliwić działanie wszystkiego innego -- więc nie spiesz się! Polecam skonfigurować swoje ograniczenia w kodzie, ponieważ dokładnie wiesz, które ograniczenia są dodawane gdzie, i o wiele łatwiej jest debugować, gdy coś pójdzie nie tak. Dodawanie ograniczeń w kodzie może być równie proste i znacznie wydajniejsze niż tworzenie interfejsów przy użyciu kotwic układu lub jednego z fantastycznych API open source dostępnych na GitHub.

  • jeśli dodajesz ograniczenia w kodzie, powinieneś to zrobić raz z poziomu updateConstraints metody twojego Podklasa UITableViewCell. Zauważ, że updateConstraints może być wywołane więcej niż raz, więc aby uniknąć dodawania tych samych ograniczeń więcej niż raz, upewnij się, że kod dodawania ograniczeń zawiera się w updateConstraints W sprawdzeniu właściwości logicznej, takiej jak didSetupConstraints (którą ustawiłeś na YES po jednokrotnym uruchomieniu kodu dodawania ograniczeń). Z drugiej strony, jeśli masz kod, który aktualizuje istniejące ograniczenia (takie jak dostosowanie właściwości constant do niektórych ograniczeń), umieść to w updateConstraints, ale poza czekiem dla didSetupConstraints, więc to może być uruchamiana za każdym razem, gdy metoda jest wywoływana.

2. Określ Unikalne Identyfikatory Ponownego Użycia Komórek Widoku Tabeli

Dla każdego unikalnego zestawu ograniczeń w komórce użyj unikalnego identyfikatora ponownego użycia komórki. Innymi słowy, jeśli twoje komórki mają więcej niż jeden unikalny układ, każdy unikalny układ powinien otrzymać swój własny identyfikator ponownego użycia. (Dobrą wskazówką, że musisz użyć nowego identyfikatora ponownego użycia, jest sytuacja, gdy wariant komórki ma inną liczbę podwijów lub podwijów są rozmieszczone w osobny Moda.)

Na przykład, jeśli wyświetlałeś wiadomość e-mail w każdej komórce, możesz mieć 4 unikalne układy: wiadomości z tematem, wiadomości z tematem i treścią, wiadomości z tematem i Załącznikiem do zdjęcia oraz wiadomości z tematem, treścią i Załącznikiem do zdjęcia. Każdy układ ma zupełnie inne ograniczenia wymagane do jego osiągnięcia, więc po zainicjowaniu komórki i dodaniu ograniczeń dla jednego z tych typów komórek komórka powinna uzyskać unikalne ponowne użycie identyfikator specyficzny dla tego typu komórki. Oznacza to, że po usunięciu komórki do ponownego użycia, ograniczenia zostały już dodane i są gotowe do użycia dla tego typu komórki.

Zauważ, że ze względu na różnice w wewnętrznej wielkości zawartości, komórki o tych samych ograniczeniach (Typ) mogą nadal mieć różną wysokość! Nie należy mylić zasadniczo różnych układów (różnych ograniczeń) z różnymi obliczonymi ramkami widoku (rozwiązanymi z identycznych ograniczeń) ze względu na różne rozmiary treść.

  • nie dodawaj komórek z zupełnie innymi zestawami ograniczeń do tej samej puli ponownego użycia (tj. używaj tego samego identyfikatora ponownego użycia), a następnie próbuj usunąć stare ograniczenia i konfigurować nowe ograniczenia od zera po każdym usunięciu. Wewnętrzny silnik układu automatycznego nie jest zaprojektowany do obsługi dużych zmian w ograniczeniach, a zobaczysz ogromne problemy z wydajnością.
Na iOS 8-self-Sizing Cells

3. Włącz Wysokość Wiersza Szacowanie

Aby włączyć SELF-sizing Table view cells, musisz ustawić widok tabeli właściwość rowHeight do UITableViewAutomaticDimension. Musisz także przypisz wartość do oszacowanej nieruchomości. Jak tylko oba te właściwości są ustawione, system wykorzystuje Układ Auto do obliczenia wysokość rzeczywista wiersza

Apple: praca z Samowyzwalającymi się komórkami widoku tabeli

[[31]}Z iOS 8, Apple zinternalizował wiele praca, która wcześniej musiała być zaimplementowana przez Ciebie przed iOS 8. Aby mechanizm SELF-sizing cell działał, należy najpierw ustawić właściwość rowHeight W widoku tabeli na stałą UITableViewAutomaticDimension. Następnie wystarczy włączyć szacowanie wysokości wiersza, ustawiając właściwość widoku tabeli estimatedRowHeight na wartość niezerową, na przykład:
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is

To, co robi, to dostarczanie widoku tabeli tymczasowego oszacowania / zastępczego dla wysokości wierszy komórek, które nie są jeszcze na ekranie. Następnie, gdy te komórki mają się przewijać na ekranie, zostanie obliczona rzeczywista wysokość wiersza. Aby określić rzeczywistą wysokość dla każdego wiersza, widok tabeli automatycznie pyta każdą komórkę, jaka wysokość jej contentView musi być oparta na znanej stałej szerokości widoku zawartości (która jest oparta na szerokości widoku tabeli, minus wszelkie dodatkowe rzeczy, takie jak indeks sekcji lub widok akcesoriów) oraz ograniczenia układu automatycznego dodane do widoku zawartości i podwidów komórki. Gdy ta rzeczywista wysokość komórki została określona, stara szacowana wysokość wiersza jest aktualizowana o nową rzeczywistą wysokość (i wszelkie korekty w widoku tabeli contentSize/contentOffset są wykonywane w razie potrzeby).

Ogólnie rzecz biorąc, podane oszacowanie nie musi być bardzo dokładne - służy tylko do prawidłowego rozmiaru wskaźnika przewijania w widoku tabeli, a widok tabeli dobrze sprawdza się w dostosowywaniu wskaźnika przewijania pod kątem nieprawidłowych szacunków podczas przewijania komórek na ekranie. Powinieneś ustawić właściwość estimatedRowHeight w widoku tabeli (w viewDidLoad lub podobnej) do stałej wartości, która jest "średnią" wysokością wiersza. tylko wtedy, gdy wysokość wiersza ma ekstremalną zmienność (np. różni się o rząd wielkości) i zauważysz wskaźnik przewijania "przeskakujący" podczas przewijania, jeśli chcesz zaimplementować tableView:estimatedHeightForRowAtIndexPath:, aby wykonać Minimalne obliczenia wymagane do zwrócenia dokładniejszego oszacowania dla każdego wiersza.

W 2007 roku firma została założona przez firmę Microsoft.]}

3. Do a Prześlij Układ I Uzyskaj Wysokość Komórki

Najpierw tworzy instancję offscreen komórki widoku tabeli, po jednej instancji dla każdego identyfikatora ponownego użycia , który jest używany wyłącznie do obliczeń wysokości. (Offscreen, co oznacza, że odwołanie do komórki jest przechowywane w właściwości / ivar kontrolera widoku i nigdy nie jest zwracane z tableView:cellForRowAtIndexPath:, aby Widok tabeli faktycznie renderował się na ekranie.) Następnie komórka musi być skonfigurowana z dokładną zawartością (np. tekst, obrazy, itp.), którą przechowywałaby, gdyby miała być wyświetlane w widoku tabeli.

Następnie Wymuś, aby komórka natychmiast rozplanowała swoje podglądy, a następnie użyj metody systemLayoutSizeFittingSize: na UITableViewCell ' S contentView, aby dowiedzieć się, jaka jest wymagana wysokość komórki. Użyj UILayoutFittingCompressedSize, aby uzyskać najmniejszy rozmiar wymagany do zmieszczenia całej zawartości komórki. Wysokość może być następnie zwrócona za pomocą metody tableView:heightForRowAtIndexPath: delegate.

4. Użyj Szacowanych Wysokości Rzędów

Jeśli twój widok tabeli zawiera więcej niż kilkadziesiąt wierszy, przekonasz się, że wykonując Automatyczne rozwiązywanie ograniczeń układu może szybko przesunąć główny wątek podczas pierwszego ładowania widoku tabeli, ponieważ tableView:heightForRowAtIndexPath: jest wywoływany w każdym wierszu przy pierwszym załadowaniu (w celu obliczenia rozmiaru wskaźnika przewijania).

Począwszy od iOS 7, Możesz (i absolutnie powinieneś) używać właściwości estimatedRowHeight w widoku tabeli. To, co robi, to dostarczenie widoku tabeli tymczasowego oszacowania/symbolu zastępczego dla wysokości wierszy komórek, które nie są jeszcze na ekranie. Następnie, gdy komórki te będą się przewijać na ekranie zostanie obliczona rzeczywista wysokość wiersza (wywołując tableView:heightForRowAtIndexPath:), a szacowana wysokość zostanie zaktualizowana o rzeczywistą.

Ogólnie rzecz biorąc, podane oszacowanie nie musi być bardzo dokładne - służy tylko do prawidłowego rozmiaru wskaźnika przewijania w widoku tabeli, a widok tabeli dobrze sprawdza się w dostosowywaniu wskaźnika przewijania pod kątem nieprawidłowych szacunków podczas przewijania komórek na ekranie. Należy ustawić właściwość estimatedRowHeight w widoku tabeli (w viewDidLoad lub podobnej) na wartość stała, która jest "średnią" wysokością wiersza. tylko wtedy, gdy wysokość wiersza ma ekstremalną zmienność (np. różni się o rząd wielkości) i zauważysz wskaźnik przewijania "przeskakujący" podczas przewijania, jeśli chcesz zaimplementować tableView:estimatedHeightForRowAtIndexPath:, aby wykonać Minimalne obliczenia wymagane do zwrócenia dokładniejszego oszacowania dla każdego wiersza.

5. (W Razie Potrzeby) Dodaj Buforowanie Wysokości Wiersza

Jeśli wykonałeś wszystkie powyższe czynności i nadal stwierdzasz, że wydajność jest niedopuszczalnie powolna, gdy rozwiązując ograniczenia w tableView:heightForRowAtIndexPath:, będziesz niestety musiał zaimplementować buforowanie dla wysokości komórek. (Takie podejście zaproponowali inżynierowie Apple.) Ogólna idea polega na tym, aby silnik układu automatycznego rozwiązał ograniczenia za pierwszym razem, a następnie buforował obliczoną wysokość dla tej komórki i używał buforowanej wartości dla wszystkich przyszłych żądań dla wysokości tej komórki. Sztuczka oczywiście polega na tym, aby upewnić się, że wyczyścisz wysokość buforowanej komórki, gdy stanie się coś, co może spowodować, że komórka wysokość do zmiany-przede wszystkim będzie to miało miejsce, gdy zmieni się zawartość tej komórki lub gdy wystąpią inne ważne zdarzenia(np. użytkownik dostosowuje suwak rozmiar tekstu dynamicznego typu).

[[96]} iOS 7 Generic przykładowy kod (z dużą ilością soczystych komentarzy)
- (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;
    }
}

Przykładowe Projekty

  • IOS 8 przykładowy projekt - wymaga iOS 8
  • W 2009 roku firma została założona przez firmę iOS.]}

Te projekty są w pełni działającymi przykładami widoków tabeli z zmienna wysokość wierszy ze względu na komórki widoku tabeli zawierające dynamiczną zawartość w Uilabelach.

Xamarin (C#/. Net)

Jeśli używasz Xamarin, sprawdź ten przykładowy projekt opracowany przez @KentBoogaart .

 2289
Author: smileyborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-24 05:24:23

Dla IOS8 jest to naprawdę proste:

override func viewDidLoad() {  
    super.viewDidLoad()

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

Lub

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

Ale dla IOS7 kluczem jest obliczenie wysokości po autolayout,

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

Ważne

  • Jeśli wiele linii etykiet, nie zapomnij ustawić numberOfLines na 0.

  • Nie zapomnij label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

Pełny przykładowy kod to tutaj .

 131
Author: William Hu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-16 13:35:18

Szybki przykład zmiennej wysokości UITableViewCell

Aktualizacja dla Swift 3

Szybka odpowiedź Williama Hu jest dobra, ale pomaga mi mieć kilka prostych, ale szczegółowych kroków podczas nauki robienia czegoś po raz pierwszy. Poniższy przykład to mój projekt testowy podczas nauki tworzenia UITableView ze zmienną wysokością komórek. Oparłem go na tym podstawowym przykładzie UITableView dla Swift.

Gotowy projekt powinien wyglądać jak to:

Tutaj wpisz opis obrazka

Utwórz nowy projekt

Może to być tylko jedna aplikacja widoku.

Dodaj kod

Dodaj nowy plik Swift do swojego projektu. Nazwij to MyCustomCell. Ta klasa będzie przechowywać punkty dla widoków, które dodajesz do swojej komórki w storyboardzie. W tym podstawowym przykładzie będziemy mieli tylko jedną etykietę w każdej komórce.

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

Podłączymy to gniazdo później.

Otwórz ViewController.swift i upewnij się, że masz następująca treść:

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).")
    }
}

Ważna Uwaga:

  • Są to dwie następujące linie kodu (wraz z automatycznym układem), które umożliwiają zmienną wysokość komórki:

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableViewAutomaticDimension
    

Setup the storyboard

Dodaj Widok tabeli do kontrolera widoku i użyj układu automatycznego, aby przypiąć go do czterech stron. Następnie przeciągnij komórkę widoku tabeli na widok tabeli. I na komórkę prototypu przeciągnij Etykietę. Użyj układu automatycznego, aby przypiąć etykietę do cztery krawędzie widoku zawartości komórki widoku tabeli.

Tutaj wpisz opis obrazka

Ważna uwaga:

  • układ Automatyczny współpracuje z dwoma ważnymi liniami kodu, o których wspomniałem powyżej. Jeśli nie używasz układu automatycznego, to nie zadziała.

Inne ustawienia IB

Niestandardowa nazwa klasy i identyfikator

Wybierz komórkę widoku tabeli i ustaw klasę niestandardową na MyCustomCell (Nazwa klasy w pliku Swift dodaliśmy). Ustaw również identyfikator na cell (ten sam ciąg znaków, który użyliśmy dla cellReuseIdentifier w powyższym kodzie.

Tutaj wpisz opis obrazka

Linie zerowe dla etykiety

Ustaw liczbę linii na 0 w etykiecie. Oznacza to wielowierszowość i umożliwia etykiecie zmianę rozmiaru w zależności od jej zawartości.

Tutaj wpisz opis obrazka

Podłącz gniazdka

  • sterowanie przeciąganiem z widoku tabeli w storyboardzie do tableView zmienna w kodzie ViewController.
  • zrób to samo dla etykiety w komórce prototypu do zmiennej myCellLabel w klasie MyCustomCell.

Zakończone

Powinieneś być w stanie uruchomić swój projekt teraz i uzyskać komórki o zmiennej wysokości.

Uwagi

  • ten przykład działa tylko dla iOS 8 i Później. Jeśli nadal potrzebujesz obsługi systemu iOS 7, nie będzie to dla ciebie działać.
  • Twoje własne komórki w przyszłych projektach będą prawdopodobnie miały więcej niż jedna wytwórnia. Upewnij się, że wszystko jest prawidłowo przypięte, aby układ automatyczny mógł określić właściwą wysokość do użycia. Może być również konieczne zastosowanie pionowej odporności na ściskanie i przytulanie. Zobacz ten artykuł aby dowiedzieć się więcej na ten temat.
  • Jeśli nie przypinasz krawędzi początkowej i końcowej (lewej i prawej), może być również konieczne ustawienie etykiety preferredMaxLayoutWidth, aby wiedziała, kiedy zawijać linię. Na przykład, jeśli do etykiety dodano ograniczenie środkowe poziomo w projekt powyżej zamiast przypinać krawędzie początkowe i końcowe, musisz dodać tę linię do metody tableView:cellForRowAtIndexPath:

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
    

Zobacz też

 75
Author: Suragch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:39

Zawinąłem rozwiązanie iOS7 @smileyborg w kategorii

Postanowiłem zawinąć to sprytne rozwiązanie @ smileyborg w kategorię UICollectionViewCell+AutoLayoutDynamicHeightCalculation.

Kategoria rozwiązuje również problemy opisane w odpowiedzi @wildmonkey (Ładowanie komórki z stalówki i systemLayoutSizeFittingSize: zwracanie CGRectZero)

Nie bierze pod uwagę żadnego buforowania, ale odpowiada moim potrzebom w tej chwili. Zapraszam do kopiowania, wklejania i hack na to.

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

Przykład użycia:

- (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);
}

Na szczęście nie będziemy musieli robić tego Jazzu w iOS8, ale na razie jest!

 61
Author: Adam Waite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-20 17:28:24

Oto moje rozwiązanie. Przed załadowaniem widoku należy podać w widoku TableView szacowaną wartość. W przeciwnym razie nie będzie w stanie zachowywać się jak oczekiwano.

- (void)viewWillAppear:(BOOL)animated {
    _messageField.delegate = self;
    _tableView.estimatedRowHeight = 65.0;
    _tableView.rowHeight = UITableViewAutomaticDimension;
}
 44
Author: eddwinpaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-13 23:56:19

Rozwiązanie zaproponowane przez @ smileyborg jest prawie idealne. Jeśli masz własną komórkę i chcesz mieć jedną lub więcej UILabel z dynamicznymi wysokościami, to metoda systemLayoutSizeFittingSize w połączeniu z włączonym AutoLayout zwraca CGSizeZero, chyba że przeniesiesz wszystkie ograniczenia komórek z komórki do contentView (jak sugeruje @TomSwift tutaj Jak zmienić rozmiar superview, aby pasował do wszystkich podwizów z autolayout?).

Aby to zrobić, musisz wstawić następujący kod w swoim niestandardowym Implementacja UITableViewCell (dzięki @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];
    }
}

Mieszanie @smileyborg odpowiedź z tym powinno działać.

 43
Author: wildmonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:39

Na tyle ważny, że wpadłem, by napisać jako odpowiedź.

@smileyborg odpowiedź jest w większości poprawna. Jeśli jednak w metodzie layoutSubviews niestandardowej klasy komórki masz jakiś kod, na przykład ustawiając preferredMaxLayoutWidth, nie będzie on uruchamiany z tym kodem:

[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
Przez jakiś czas mnie to zawstydzało. Potem zdałem sobie sprawę, że to dlatego, że wywołują one tylko layoutSubviews na contentView, a nie samą komórkę.

Mój kod roboczy wygląda tak:

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

Zauważ, że jeśli tworzysz nową komórkę, jestem prawie pewien, że nie musisz wywoływać setNeedsLayout, ponieważ powinna być już ustawiona. W przypadku, gdy zapisujesz odniesienie do komórki, prawdopodobnie powinieneś zadzwonić. Tak czy siak, to nie powinno zaszkodzić.

Kolejna wskazówka, jeśli używasz podklas komórek, gdzie ustawiasz rzeczy takie jak preferredMaxLayoutWidth. Jak wspomina @smileyborg, "Twoja komórka widoku tabeli nie ma jeszcze swojej szerokości ustalonej do szerokości widoku tabeli". To prawda, i kłopot, jeśli wykonujesz swoją pracę w swojej podklasie, a nie w kontrolerze widoku. Można jednak po prostu ustawić ramkę komórki w tym miejscu, używając szerokości tabeli:

Na przykład w obliczeniach wysokości:

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);

(zdarza mi się buforować tę konkretną komórkę do ponownego użycia, ale to nieistotne).

 23
Author: Bob Spryn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-03 06:03:00

Na wypadek, gdyby ludzie nadal mieli z tym problemy. Napisałem szybki post na blogu o używaniu Autolayout z Uitableviews wykorzystując Autolayout dla dynamicznych wysokości komórek , a także komponent open source, aby pomóc to bardziej abstrakcyjne i łatwiejsze do wdrożenia. https://github.com/Raizlabs/RZCellSizeManager

 20
Author: Alex Rouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-05 15:53:36

(dla Xcode 8.x / Xcode 9.x czytane na dole)

Uwaga na następujący problem w Xcode 7.x, co może być źródłem nieporozumień:

Interface Builder nie radzi sobie z automatyczną konfiguracją komórek. Nawet jeśli Twoje ograniczenia są absolutnie ważne, IB nadal będzie narzekać i daje mylące sugestie i błędy. Powodem jest to, że IB nie chce zmieniać wysokości wiersza zgodnie z Twoimi ograniczeniami (aby komórka pasowała do Twojej treści). Zamiast tego utrzymuje stałą wysokość wiersza i zaczyna sugerować zmianę ograniczeń, co powinieneś zignorować.

Na przykład wyobraź sobie, że ustawiłeś wszystko w porządku, bez ostrzeżeń, bez błędów, wszystko działa.

Tutaj wpisz opis obrazka

Teraz jeśli zmienisz rozmiar czcionki (w tym przykładzie zmieniam rozmiar czcionki etykiety opisu z 17.0 na 18.0).

Tutaj wpisz opis obrazka

Ponieważ rozmiar czcionki się zwiększył, Etykieta chce teraz zajmować 3 wiersze (wcześniej zajmował 2 rzędy).

Jeśli Interface Builder działał zgodnie z oczekiwaniami, zmieniłby rozmiar komórki, aby dostosować ją do nowej wysokości etykiety. Jednak to, co się dzieje, to to, że IB wyświetla czerwoną ikonę błędu auto-layout i sugeruje zmodyfikowanie priorytetów Przytulania/kompresji.

Tutaj wpisz opis obrazka

Powinieneś zignorować te ostrzeżenia. Zamiast tego możesz * ręcznie zmienić wysokość wiersza (wybierz Komórka > Inspektor rozmiaru > wiersz Wysokości).

Tutaj wpisz opis obrazka

Zmieniałem tę wysokość jednym kliknięciem na raz (za pomocą steppera góra/dół), aż błędy czerwonej strzałki znikną! (w rzeczywistości otrzymasz żółte ostrzeżenia, w tym momencie po prostu idź do przodu i zrób "Aktualizuj Ramki", to wszystko powinno działać).

* zauważ, że nie musisz rozwiązywać tych czerwonych błędów lub żółtych ostrzeżeń w Interface Builder - w czasie wykonywania wszystko będzie działać poprawnie (nawet jeśli IB pokazuje błędy/ostrzeżenia). Tylko upewnij się, że w czas pracy w dzienniku konsoli nie występują błędy AutoLayout.

W rzeczywistości próba aktualizacji wysokości wiersza w IB jest bardzo irytująca, a czasami prawie niemożliwa (z powodu wartości ułamkowych).

Aby zapobiec irytującym ostrzeżeniom/błędom IB, możesz wybrać widoki zaangażowane i w {[0] } dla właściwości Ambiguity Wybierz Verify Position Only

Tutaj wpisz opis obrazka


Xcode 8.x / Xcode 9.x wydaje się (czasami) robić rzeczy inaczej niż Xcode 7.x, ale nadal niepoprawnie. Na przykład nawet wtedy, gdy compression resistance priority / hugging priority Konfigurator interfejsu może rozciągać lub przycinać etykietę, aby pasowała do komórki (zamiast zmieniać rozmiar komórki, aby pasowała do etykiety). W takim przypadku może nawet nie wyświetlać żadnych ostrzeżeń lub błędów AutoLayout. A czasami robi dokładnie to, co Xcode 7.x zrobił, opisane powyżej.
 18
Author: Nikolay Suvandzhiev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-25 08:47:55

Tak długo, jak twój układ w komórce jest dobry.

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];

    return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}

Aktualizacja: powinieneś użyć dynamicznej zmiany rozmiaru wprowadzonej w systemie iOS 8.

 17
Author: Chris Van Buskirk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-12 22:11:35

Jak @Bob-Spryn natknąłem się na na tyle ważne, że zamieszczam to jako odpowiedź.

Przez jakiś czas zmagałem się z@smileyborg ' s odpowiedz. Jeśli zdefiniowałeś swoją prototypową komórkę w IB z dodatkowymi elementami (UILabels, UIButtons, itd.) w IB, gdy tworzysz instancję komórki za pomocą [[YourTableViewCellClass alloc] init], nie tworzy ona instancji wszystkich innych elementów w tej komórce, chyba że napisałeś w tym celu kod. (Miałem podobne doświadczenie z initWithStyle.)

Aby storyboard stworzył instancję wszystkich dodatkowych elementów, uzyskaj komórkę za pomocą [tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"] (nie [tableView dequeueReusableCellWithIdentifier:forIndexPath:], ponieważ spowoduje to interesujące problemy.) Kiedy to zrobisz, wszystkie elementy zdefiniowane w IB zostaną utworzone.

 15
Author: nickb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:33

Dynamiczny widok tabeli wysokość komórki i automatyczny układ

Dobry sposób na rozwiązanie problemu z układem automatycznym storyboard:

- (CGFloat)heightForImageCellAtIndexPath:(NSIndexPath *)indexPath {
  static RWImageCell *sizingCell = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sizingCell = [self.tableView dequeueReusableCellWithIdentifier:RWImageCellIdentifier];
  });

  [sizingCell setNeedsLayout];
  [sizingCell layoutIfNeeded];

  CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
  return size.height;
}
 14
Author: Mohnasm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-05 09:34:38

Aby ustawić automatyczny wymiar dla wysokości wiersza i szacowanej wysokości wiersza, upewnij się, że wykonasz następujące kroki, aby wymiar automatyczny był skuteczny dla układu wysokości komórki/wiersza.

  • Przypisywanie i implementowanie tableview dataSource i delegowanie
  • przypisanieUITableViewAutomaticDimension do rowHeight & estimatedRowHeight
  • zaimplementuj metody delegate/dataSource (tzn. heightForRowAt i zwróć do niej wartość UITableViewAutomaticDimension)

-

Cel C:

// in ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

  @property IBOutlet UITableView * table;

@end

// in ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.table.dataSource = self;
    self.table.delegate = self;

    self.table.rowHeight = UITableViewAutomaticDimension;
    self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return UITableViewAutomaticDimension;
}

Swift:

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    // Swift 4.2 onwards
    table.rowHeight = UITableView.automaticDimension
    table.estimatedRowHeight = UITableView.automaticDimension


    // Swift 4.1 and below
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension

}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    // Swift 4.2 onwards
    return UITableView.automaticDimension

    // Swift 4.1 and below
    return UITableViewAutomaticDimension
}

Dla instancja etykiety w UITableviewCell

  • Set number of lines = 0 (&line break mode = truncate tail)
  • Ustaw wszystkie ograniczenia (góra, dół, prawy lewy) w odniesieniu do jego kontenera superview/ cell.
  • opcjonalne : ustaw minimalną wysokość etykiety, jeśli chcesz, aby minimalna powierzchnia pionowa była pokryta etykietą, nawet jeśli nie ma danych.

Tutaj wpisz opis obrazka

Uwaga: jeśli masz więcej niż jedną etykietę (UIElements) o dynamicznej długości, która powinna być dostosowana w zależności od rozmiaru zawartości: Dostosuj "priorytet Przytulania zawartości i odporności na ściskanie" dla etykiet, które chcesz rozszerzyć/skompresować z wyższym priorytetem.

 14
Author: Krunal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-05 14:23:22

Inne "rozwiązanie": pomiń całą tę frustrację i użyj UIScrollView zamiast tego, aby uzyskać wynik, który wygląda i wydaje się identyczny z UITableView.

To było bolesne "rozwiązanie" dla mnie, po tym, jak włożyłem dosłownie 20+ bardzo frustrujące godziny, próbując zbudować coś takiego, co zasugerował smileyborg i zawodząc przez wiele miesięcy i trzy wersje wydań App Store.

Moim zdaniem, jeśli naprawdę potrzebujesz wsparcia dla iOS 7 (dla nas jest to niezbędne), technologia jest po prostu zbyt kruche i będziesz ciągnąć włosy próbując. I że UITableView jest kompletnym przesadą, chyba że używasz niektórych zaawansowanych funkcji edycji wierszy i / lub naprawdę potrzebujesz obsługi 1000 + "wierszy" (w naszej aplikacji, to realistycznie nigdy nie więcej niż 20 wierszy).

Dodatkową zaletą jest to, że kod staje się szalenie prosty w porównaniu z całym gównem delegatów i z powrotem, które pochodzi z UITableView. To tylko jedna pętla kodu w viewOnLoad, która wygląda elegancko i jest łatwa do Zarządzaj.

Oto kilka wskazówek jak to zrobić:

1) używając Storyboard lub pliku nib, Utwórz kontroler widoku i powiązany z nim widok główny.

2) przeciągnij UIScrollView do widoku głównego.

3) Dodaj ograniczenia góra, dół, lewo i prawo do widoku najwyższego poziomu tak, aby UIScrollView wypełnił cały widok główny.

4) Dodaj UIView wewnątrz UIScrollView i nazwij go "kontenerem". Dodaj górne, dolne, lewe i prawe ograniczenia do widoku UIScrollView (jego rodzica). Kluczowy TRICK: Dodaj również ograniczenia "równe szerokości", aby połączyć UIScrollView i UIView.

Pojawi się błąd "widok przewijania ma niejednoznaczną wysokość przewijanej zawartości" i że Twój kontener UIView powinien mieć wysokość 0 pikseli. Żaden błąd nie ma znaczenia, gdy aplikacja jest uruchomiona.

5) twórz pliki nib i kontrolery dla każdej z twoich "komórek". Użyj UIView, a nie UITableViewCell.

5) w głównym kontrolerze ViewController, zasadniczo dodajesz wszystkie "wiersze" do kontener UIView i programowo dodają ograniczenia łączące ich lewą i prawą krawędź z widokiem kontenera, ich górne krawędzie z widokiem kontenera na górze (dla pierwszego elementu) lub poprzedniej komórki. Następnie połącz ostatnią komórkę z dnem pojemnika.

Dla nas, każdy "wiersz" jest w pliku stalówki. Więc kod wygląda mniej więcej tak:

class YourRootViewController {

    @IBOutlet var container: UIView! //container mentioned in step 4

    override func viewDidLoad() {

        super.viewDidLoad()

        var lastView: UIView?
        for data in yourDataSource {

            var cell = YourCellController(nibName: "YourCellNibName", bundle: nil)
            UITools.addViewToTop(container, child: cell.view, sibling: lastView)
            lastView = cell.view
            //Insert code here to populate your cell
        }

        if(lastView != nil) {
            container.addConstraint(NSLayoutConstraint(
                item: lastView!,
                attribute: NSLayoutAttribute.Bottom,
                relatedBy: NSLayoutRelation.Equal,
                toItem: container,
                attribute: NSLayoutAttribute.Bottom,
                multiplier: 1,
                constant: 0))
        }

        ///Add a refresh control, if you want - it seems to work fine in our app:
        var refreshControl = UIRefreshControl()
        container.addSubview(refreshControl!)
    }
}

A oto kod do UITools.addViewToTop:

class UITools {
    ///Add child to container, full width of the container and directly under sibling (or container if sibling nil):
    class func addViewToTop(container: UIView, child: UIView, sibling: UIView? = nil)
    {
        child.setTranslatesAutoresizingMaskIntoConstraints(false)
        container.addSubview(child)

        //Set left and right constraints so fills full horz width:

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Leading,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Left,
            multiplier: 1,
            constant: 0))

        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Trailing,
            relatedBy: NSLayoutRelation.Equal,
            toItem: container,
            attribute: NSLayoutAttribute.Right,
            multiplier: 1,
            constant: 0))

        //Set vertical position from last item (or for first, from the superview):
        container.addConstraint(NSLayoutConstraint(
            item: child,
            attribute: NSLayoutAttribute.Top,
            relatedBy: NSLayoutRelation.Equal,
            toItem: sibling == nil ? container : sibling,
            attribute: sibling == nil ? NSLayoutAttribute.Top : NSLayoutAttribute.Bottom,
            multiplier: 1,
            constant: 0))
    }
}

Jedyną "gotcha" jaką znalazłem z tym podejściem do tej pory jest to, że UITableView ma ładną funkcję "pływających" nagłówków sekcji u góry widoku podczas przewijania. Powyższe rozwiązanie tego nie zrobi, jeśli nie dodasz więcej programowania, ale w naszym konkretnym przypadku ta funkcja nie była w 100% niezbędna i nikt nie zauważył, kiedy odeszła.

Jeśli chcesz dzielniki między komórkami, po prostu dodaj 1 piksel wysoki UIView na dole niestandardowej "komórki", która wygląda jak dzielnik.

Pamiętaj, aby włączyć "bounces" i "bounce vertical", aby Kontrola odświeżania działała i więc wydaje się bardziej jak widok tabeli.

TableView pokazuje kilka pustych wierszy i dzielników pod Twoją zawartością, jeśli nie wypełnia pełnego ekranu, gdzie to rozwiązanie nie. ale osobiście wolę, jeśli te puste wiersze nie były tam i tak - przy zmiennej wysokości komórki zawsze wydawało mi się" buggy", aby mieć puste wiersze tam.

Mam nadzieję, że jakiś inny programista czyta mój post, zanim zmarnuje 20 + godzin, próbując to rozgryźć za pomocą widoku tabeli w swojej własnej aplikacji. :)

 12
Author: alpsystems.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-14 22:17:20
tableView.estimatedRowHeight = 343.0
tableView.rowHeight = UITableViewAutomaticDimension

Tutaj wpisz opis obrazka

 10
Author: Abo3atef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 12:43:50

Musiałem użyć dynamicznych widoków (widoki konfiguracji i ograniczenia według kodu) i kiedy chciałem ustawić preferredMaxLayoutWidth szerokości etykiety było 0. Więc mam złą wysokość komórki.

Potem dodałem

[cell layoutSubviews];

Przed wykonaniem

[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];

Po tej etykiecie szerokość była zgodna z oczekiwaniami, a dynamiczna wysokość była obliczana prawidłowo.

 9
Author: Mansurov Ruslan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-01 22:37:52

Załóżmy, że masz komórkę z subviewem i chcesz, aby jej wysokość była wystarczająco wysoka, aby obejmowała subview + padding.

1) Ustaw dolne ograniczenie subview równe komórce.contentView minus padding, który chcesz. Nie ustawiaj ograniczeń na komórce lub komórce.contentView itself.

2) ustaw właściwość tableView rowHeight LUB tableView:heightForRowAtIndexPath: na UITableViewAutomaticDimension.

3) Ustaw albo Właściwość tableView estimatedRowHeight albo tableView:estimatedHeightForRowAtIndexPath:, Aby najlepiej zgadnąć wysokość.

To to.

 6
Author: Vadoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-18 17:37:44

Jeśli układasz programowo, oto, co należy wziąć pod uwagę w przypadku iOS 10 za pomocą kotwic w języku Swift.

Istnieją trzy zasady / kroki

Numer 1: Ustaw te dwie właściwości tableview na viewDidLoad, pierwsza mówi do tableview, który powinien oczekiwać dynamicznych rozmiarów na swoich komórkach, druga jest po prostu pozwolić aplikacji obliczyć rozmiar wskaźnika paska przewijania, więc pomaga to w wydajności.

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

Numer 2: jest to ważne, musisz dodać podgląd zawartości komórki nie do widoku, a także użyj jej layoutsmarginguide do zakotwiczenia podglądów na górze i na dole, jest to roboczy przykład, jak to zrobić.

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setUpViews()
}

private func setUpViews() {

    contentView.addSubview(movieImageView)
    contentView.addSubview(descriptionLabel)
    let marginGuide = contentView.layoutMarginsGuide

    NSLayoutConstraint.activate([
        movieImageView.heightAnchor.constraint(equalToConstant: 80),
        movieImageView.widthAnchor.constraint(equalToConstant: 80),
        movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
        movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),

        descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
        descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
        descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
        descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)

        ])
}

Utwórz metodę, która doda Podglądy i wykona układ, wywołując go w metodzie init.

NUMER 3: NIE WYWOŁAJ METODY:

  override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    }

Jeśli to zrobisz, nadpiszesz swoją implementację.

Postępuj zgodnie z tymi 3 regułami dla komórek dynamicznych w widoku tableviews.

Oto realizacja robocza https://github.com/jamesrochabrun/MinimalViewController

 3
Author: James Rochabrun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-28 15:59:47

W moim przypadku muszę stworzyć własną komórkę z obrazem pochodzącym z serwera i może mieć dowolną szerokość i wysokość. I dwa Uilabele o dynamicznym rozmiarze (zarówno szerokość jak i wysokość)

To samo osiągnąłem tutaj w odpowiedzi z autolayout i programowo:

Zasadniczo powyżej @smileyBorg odpowiedź pomogła, ale systemLayoutSizeFittingSize nigdy nie działał dla mnie, w moim podejściu:

1. Brak użycia właściwości automatycznego obliczania wysokości wiersza. 2.No zastosowanie szacowanej wysokości 3.No potrzeba niepotrzebnych updateConstraints. 4.No zastosowanie automatycznej preferowanej maksymalnej szerokości układu. 5. No use of systemLayoutSizeFittingSize (should have use but not working for me, I dont know what it is doing internally), but instead my method - (float)getViewHeight working and I know what it ' s doing internally.

Czy można mieć różną wysokość w komórce UITableView, gdy używam kilku różnych sposobów wyświetlania komórki?

 0
Author: Alok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:39

W moim przypadku padding był spowodowany wysokością sectionHeader i sectionFooter, gdzie storyboard pozwolił mi zmienić go na minimum 1. Tak więc w metodzie viewDidLoad:

tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0
 0
Author: Rashid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-25 07:35:59

Zrobiłem jakąś głupią próbę i błąd z 2 wartościami rowHeight i estimatedRowHeight i pomyślałem, że może to dostarczyć trochę debugowania:

Jeśli ustawisz oba lub tylko estimatedRowHeight, otrzymasz pożądane zachowanie:

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1.00001 // MUST be greater than 1

Sugeruje się, aby zrobić wszystko, co w twojej mocy, aby uzyskać poprawne oszacowanie, ale wynik końcowy nie jest inny. To tylko wpłynie na Twoją wydajność.

Tutaj wpisz opis obrazka


Jeśli ustawisz tylko rowHeight ie tylko do:

tableView.rowHeight = UITableViewAutomaticDimension

Twój efekt końcowy nie będzie taki, jak chcesz:

Tutaj wpisz opis obrazka


Jeśli ustawisz estimatedRowHeight na 1 lub mniejszy, tozawiesisz niezależnie od rowHeight.

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 1 

I crashed with the following error message:

Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'table view row height
must not be negative - provided height for index path (<NSIndexPath:
0xc000000000000016> {length = 2, path = 0 - 0}) is -1.000000'
    ...some other lines...

libc++abi.dylib: terminating with uncaught exception of type
NSException
 0
Author: Honey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-25 01:56:43

Odnośnie zaakceptowanej odpowiedzi przez @ smileyborg, znalazłem

[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]

Być nierzetelnym w niektórych przypadkach, gdy ograniczenia są niejednoznaczne. Lepiej wymusić na silniku układu obliczenie wysokości w jednym kierunku, używając kategorii helper na poniższym UIView:

-(CGFloat)systemLayoutHeightForWidth:(CGFloat)w{
    [self setNeedsLayout];
    [self layoutIfNeeded];
    CGSize size = [self systemLayoutSizeFittingSize:CGSizeMake(w, 1) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    CGFloat h = size.height;
    return h;
}

Gdzie w: jest szerokością widoku tabeli

 0
Author: railwayparade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-18 22:31:26

Jeśli masz długi sznurek. np. taki, który nie ma przerwania linii. Wtedy możesz napotkać pewne problemy.

Poprawka jest wymieniona przez zaakceptowaną odpowiedź i kilka innych odpowiedzi. Wystarczy dodać

cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
Uważam, że odpowiedź Suragha jest najbardziej kompletna, a jednocześnie najmniej myląca.

Choć nie wyjaśniają dlaczego . Zróbmy to.

Wrzuć poniższy kod do projektu.

import UIKit

class ViewController: UIViewController {

    lazy var label : UILabel = {
        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.backgroundColor = .red
        lbl.textColor = .black
        return lbl
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // step0: (0.0, 0.0)
        print("empty Text intrinsicContentSize: \(label.intrinsicContentSize)")
        // step1: (29.0, 20.5)
        label.text = "hiiiii"
        print("hiiiii intrinsicContentSize: \(label.intrinsicContentSize)")
        // step2: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints"
        print("1 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // step3: (992.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints"
        print("3 translate intrinsicContentSize: \(label.intrinsicContentSize)")
        // step4: (328.0, 20.5)
        label.text = "translatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints"
        print("3 translate w/ line breaks intrinsicContentSize: \(label.intrinsicContentSize)") 
        // step5: (328.0, 61.0)
        label.numberOfLines = 0
        print("3 translate w/ line breaks and '0' numberOfLines intrinsicContentSize: \(label.intrinsicContentSize)") 
        // step6: (98.5, 243.5)
        label.preferredMaxLayoutWidth = 100
        print("3 translate w/ line breaks | '0' numberOfLines | preferredMaxLayoutWidth: 100 intrinsicContentSize: \(label.intrinsicContentSize)") 

        setupLayout()
    }
    func setupLayout(){
        view.addSubview(label)
        label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
}

Zauważ, że nie dodałem żadnych Rozmiar Dodałem tylko centerx, centery. Ale nadal etykieta będzie miała prawidłowy rozmiar dlaczego? Z powodu contentSize.

Aby lepiej to przetworzyć, najpierw zachowaj krok 0, a następnie skomentuj kroki 1-6. Niech setupLayout() zostanie. Obserwuj zachowanie.

Następnie odkomentuj Krok 1 i obserwuj.

Następnie odkomentuj Krok 2 i obserwuj.

Rób to, dopóki nie skomentujesz wszystkich 6 kroków i nie zaobserwujesz ich zachowań.

Co można wywnioskować ze wszystkich to? Jakie czynniki mogą zmienić contenSize?

  1. Długość tekstu: jeśli masz dłuższy tekst, to wewnętrzny rozmiar Szerokość zwiększy
  2. podziały linii: jeśli dodasz \n, to szerokość intrinsicContentSize będzie maksymalną szerokością wszystkich linii. Jeśli jedna linia ma 25 znaków, inna ma 2 znaki, a inna ma 21 znaków, wtedy szerokość zostanie obliczona na podstawie 25 znaków
  3. liczba dozwolonych linie: musisz ustawić numberOfLines na 0 w przeciwnym razie nie będziesz miał wielu linii. Twój numberOfLines dostosuje swój wewnętrzny rozmiar wysokość
  4. Dokonywanie korekt: wyobraź sobie, że na podstawie Twojego tekstu, szerokość twojego wewnętrznego contentsize wynosiła 200, A Wysokość 100, ale chciałeś ograniczyć szerokość do pojemnika etykiety co zamierzasz zrobić? Rozwiązaniem jest ustawienie go na żądaną szerokość. Robisz to ustawiając preferredMaxLayoutWidth do 130 wtedy twój nowy intrinsicContentSize będzie miał szerokość mniej więcej 130. Wysokość byłaby oczywiście większa niż 100, ponieważ potrzebowałbyś więcej linii. Mając to na uwadze, jeśli Twoje ograniczenia są ustawione poprawnie, nie będziesz musiał tego w ogóle używać! Więcej na ten temat można znaleźć w ta odpowiedź i jej komentarze. Musisz tylko użyć preferredMaxLayoutWidth, jeśli nie masz ograniczeń ograniczających szerokość/wysokość, jak w jednym może powiedzieć " nie zawijaj tekstu, chyba że przekracza preferredMaxLayoutWidth". Ale ze 100% pewnością jeśli Ustaw początek / koniec i numberOfLines na {[7] } wtedy jesteś dobry! długa historia krótka większość odpowiedzi tutaj, które zalecają korzystanie z niego są błędne! Nie potrzebujesz tego. Potrzeba to znak, że Twoje ograniczenia nie są ustawione poprawnie lub po prostu nie masz ograniczeń

  5. Rozmiar czcionki: zauważ również, że jeśli zwiększysz wielkość czcionki, wzrośnie wewnętrzna wysokość czcionki . Nie pokazałem tego w kodzie. Możesz spróbować na swoim własne.

Więc wróć do przykładu tableViewCell:

Wszystko, co musisz zrobić, to ustawić numberOfLines na 0 i ustawić preferredMaxLayoutWidth na tableView'S width.

 0
Author: Honey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-09 14:32:30

Po prostu dodaj te dwie funkcje do kontrolera viewcontroller to rozwiąże twój problem. Tutaj list jest tablicą łańcuchów, która zawiera twój ciąg każdego wiersza.

 func tableView(_ tableView: UITableView, 
   estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        tableView.rowHeight = self.calculateHeight(inString: list[indexPath.row])

    return (tableView.rowHeight) 
}

func calculateHeight(inString:String) -> CGFloat
{
    let messageString = input.text
    let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 15.0)]

    let attributedString : NSAttributedString = NSAttributedString(string: messageString!, attributes: attributes)

    let rect : CGRect = attributedString.boundingRect(with: CGSize(width: 222.0, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)

    let requredSize:CGRect = rect
    return requredSize.height
}
 -1
Author: Parth Barot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 06:08:31

Jeszcze jedno rozwiązanie iOs7+iOs8 w języku Swift

var cell2height:CGFloat=44

override func viewDidLoad() {
    super.viewDidLoad()
    theTable.rowHeight = UITableViewAutomaticDimension
    theTable.estimatedRowHeight = 44.0;
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCellWithIdentifier("myTableViewCell", forIndexPath: indexPath) as! myTableViewCell
    cell2height=cell.contentView.height
    return cell
}

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if #available(iOS 8.0, *) {
        return UITableViewAutomaticDimension
    } else {
        return cell2height
    }
}
 -4
Author: djdance,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 07:20:21