Niestandardowe ustawienie wysokości wiersza komórki w serii ujęć nie odpowiada

213

Próbuję dostosować wysokość komórki dla jednej z komórek w moim widoku tabeli. Dostosowuję rozmiar z ustawienia „wysokości wiersza” w „inspektorze wielkości” danej komórki. Po uruchomieniu aplikacji na iPhonie komórka ma domyślny rozmiar ustawiony na podstawie „rozmiaru wiersza” w widoku tabeli.

Jeśli zmienię „rozmiar wiersza” widoku tabeli, wówczas zmieni się rozmiar wszystkich komórek. Nie chcę tego robić, ponieważ chcę niestandardowy rozmiar tylko dla jednej komórki. Widziałem wiele postów, które mają programowe rozwiązanie problemu, ale wolałbym to zrobić za pomocą scenorysu, jeśli to możliwe.

zirinisp
źródło

Odpowiedzi:

295

W komórkach dynamicznychrowHeight ustawione w UITableView zawsze nadpisuje wartość rowHeight poszczególnych komórek.

Ale na statycznych komórkach, rowHeightustawione na poszczególnych komórkach mogą zastąpić UITableView.

Nie jesteś pewien, czy to błąd, Apple może celowo to robić?

pixelfreak
źródło
36
Prawidłowa odpowiedź nr 3, a konkretnie dlatego, że ta odpowiedź dotyczy Konstruktora interfejsów / Storyboardów. Jeśli zaznaczysz komórkę w IB, a następnie Rozmiar Inspector pokazuje Wysokość wiersza na górze (z „niestandardowym” checkbox), ale jeśli wybierzesz całą tabelę zobaczyć ten Wielkość Inspector pokazy Row Wysokość w szczycie tam (nie „niestandardowego " w tym przypadku). Jak mówi pixelfreak, do dynamicznych komórek używane jest tylko ustawienie widoku tabeli. (Nie jestem pewien, czy to celowe)
Rabarbar
8
Ta odpowiedź sugeruje, że rozwiązaniem byłoby po prostu zmienić UITableViewtreść od Dynamic Prototypescelu Static Cells, zrobiłem to, a cały mój projekt wysadzili .. prawie zginął siebie.
abbood 31.10.13
4
Czy jest jakiś sposób na odzyskanie tego numeru? Czy jedynym sposobem na głębokie kopanie jest zanurzenie się w scenorysie i wyciągnięcie stamtąd?
Biclops,
To zdecydowanie NIE jest błąd, ponieważ twoja tabela jest dynamiczna, więc skąd system mógł wiedzieć, gdzie poszedłeś, użyć każdej z tych komórek? I ile razy użyłby każdego prototypu.
Vincent Bernier
Wydaje się również występować problem, jeśli początkowo skonfigurujesz widok tabeli jako „komórki statyczne”, a następnie zmienisz go na „dynamiczne prototypy”. Miałem problem polegający na tym, że nawet metoda delegowania dla rowHeight była ignorowana. Najpierw rozwiązałem go, edytując bezpośrednio scenariuszowy kod XML, a na koniec po prostu przebudowałem scenę od podstaw, używając dynamicznych prototypów od samego początku.
Eric Goldberg,
84

Jeśli używasz UITableViewController, zaimplementuj tę metodę:

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

W funkcji wiersza możesz wybrać Wysokość. Na przykład,

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}

W tym przykładzie wysokość pierwszego rzędu wynosi 100 pikseli, a pozostałe 60 pikseli.

Mam nadzieję, że ten może ci pomóc.

Bursztyn
źródło
2
Beber, czy jeden ZAWSZE musi robić oba te elementy (zaznacz opcję Niestandardowa i edytuj wartość Wysokość wiersza w serii ujęć i & podaj ją w heightForRowAtIndexPath)?
marciokoko,
Czy możesz mi pomóc w tej sprawie? Muszę mieć dynamiczne komórki o dynamicznej wysokości, ale jeśli użyję tej metody, bez względu na to, co zwrócę, wszystkie komórki znikną. Z wyjątkiem iPhone'a 5 urządzenie , w którym działa zgodnie z oczekiwaniami. Czy rozumiesz dlaczego?
Ostmeistro,
34

W przypadku komórek dynamicznych rowHeightustawienie UITableViewzawsze zastępuje poszczególne komórki rowHeight.

Takie zachowanie jest, IMO, błędem. Za każdym razem, gdy musisz zarządzać interfejsem użytkownika w dwóch miejscach, jest on podatny na błędy. Na przykład, jeśli zmienisz rozmiar komórki w serii ujęć, musisz pamiętać, aby również je zmienić heightForRowAtIndexPath:. Dopóki Apple nie naprawi błędu, obecnie najlepszym obejściem jest przesłonięcie heightForRowAtIndexPath:, ale użyj rzeczywistych prototypowych komórek z serii ujęć, aby określić wysokość, zamiast używać liczb magicznych . Oto przykład:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

Zapewni to, że wszelkie zmiany wysokości prototypowych komórek zostaną automatycznie wykryte w czasie wykonywania, a użytkownik będzie musiał zarządzać interfejsem użytkownika tylko w jednym miejscu: w scenariuszu.

memmons
źródło
2
Wysłałem odpowiedź z pełnym kodem, w tym buforowaniem; patrz stackoverflow.com/a/16881312/292166
JosephH
1
Wow wow wow! Poparłem odpowiedź, ale bądźcie ŚWIADOMI! Ta metoda powoduje nie tylko obniżenie wydajności, jak powiedział @Brennan w komentarzach, ale także powoduje zwiększenie alokacji pamięci przy każdym przeładowaniu Dane, takie jak wyciek pamięci! Konieczne jest skorzystanie z powyższego obejścia obiektywovet! Spędź dzień, aby złapać wyciek pamięci!
skywinder
Nie działa w Swift, ponieważ cell.bounds.size.height zawsze zwraca 0.0
King-Wizard
1
Komórki jeszcze nie istnieją w momencie, gdy system nazywał metodę tableView: heightForRowAtIndexPath. Powinieneś być w stanie użyć przekazanej Ci metody indexPath, aby zindeksować model danych i zobaczyć, jaka komórka zostanie tam użyta, a następnie obliczyć wysokość tej komórki i zwrócić ją. Umożliwia to układowi układ komórek w tabeli przed utworzeniem komórek.
King-Wizard
1
Ponadto nie powinieneś wywoływać własnej metody cellForRowAtIndexPath. Widoki tabel mają metodę cellForRowAtIndexPath, która zwróci komórkę dla tej ścieżki indeksu, jeśli jest ona obecnie widoczna na ekranie, ale często ta ścieżka indeksu nie jest z nią powiązana.
King-Wizard
22

Zbudowałem kod, na który wskazują różne odpowiedzi / komentarze, aby działało to w przypadku scenorysów wykorzystujących komórki prototypowe.

Ten kod:

  • Nie wymaga ustawiania wysokości komórki w innym miejscu niż oczywiste miejsce w serii ujęć
  • Buforuje wysokość ze względu na wydajność
  • Używa wspólnej funkcji, aby uzyskać identyfikator komórki dla ścieżki indeksu, aby uniknąć powielonej logiki

Dzięki Answerbot, Brennan i lensovet.

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>
JosephH
źródło
Wiem, że to stara odpowiedź, ale czy ktoś inny uważa, że ​​powoduje to przepełnienie stosu? Gdy widok się ładuje, otrzymuję UISectionRowData refreshWithSection: tableViewRowData: która wywołuje tableView: heightForRowAtIndexPath: która wywołuje dequeueReusableCellWithIdentifier: która wywołuje [UISectionRowData ...], więc mam przepełnienie stosu rekurencyjnego. Naprawdę chciałem użyć tego rozwiązania, ale wydaje się, że nie działa ono z iOS7.
sbaker
Nie działa w Swift, ponieważ cell.bounds.size.height zawsze zwraca 0.0
King-Wizard
Komórki jeszcze nie istnieją w momencie, gdy system nazywał metodę tableView: heightForRowAtIndexPath. Powinieneś być w stanie użyć przekazanej Ci metody indexPath, aby zindeksować model danych i zobaczyć, jaka komórka zostanie tam użyta, a następnie obliczyć wysokość tej komórki i zwrócić ją. Umożliwia to układowi układ komórek w tabeli przed utworzeniem komórek.
King-Wizard
Ponadto nie powinieneś wywoływać własnej metody cellForRowAtIndexPath. Widoki tabel mają metodę cellForRowAtIndexPath, która zwróci komórkę dla tej ścieżki indeksu, jeśli jest ona obecnie widoczna na ekranie, ale często ta ścieżka indeksu nie jest z nią powiązana.
King-Wizard
@ snow-tiger Nie próbowałem tego szybko, ale nie rozumiem twoich komentarzy. Nie „wywołuję [mojej] własnej metody cellForRowAtIndexPath” i celowo tego nie robię. Wiem również, że komórki nie istnieją w czasie timeView: wysokość jest wywoływana wysokośćForRowAtIndexPath, to jest cały punkt tego kodu i dlaczego używa dequeueReusableCellWithIdentifier do uzyskania komórki. Kod podany w tej odpowiedzi działa. Albo coś się dzieje szybko, coś jest nie tak w szybkiej konwersji, albo próbujesz rozwiązać inny problem, na który celuje to pytanie / odpowiedź.
JosephH
19

W rzeczywistości są dwa miejsca, w których musisz zmienić wysokość wiersza, najpierw komórka (już to zmieniłeś), a teraz wybierz Widok tabeli i sprawdź Inspektor rozmiaru

Kristjan H.
źródło
w widoku tabeli (nie widoku komórki)> w zakładce inspektora rozmiarów> wysokość wiersza
iman kazemayni
Doprowadza mnie do szału za każdym razem, gdy zapominam o tym szczególe. Nie mam pojęcia, dlaczego dostosowanie komórki nie aktualizuje tableView automatycznie podczas korzystania ze scenariuszy!
Skuter
11

Myślę, że to błąd.

Spróbuj wyregulować wysokość nie za pomocą Inspektora narzędzi, ale przeciągając myszą bezpośrednio na scenorysie.

Rozwiązałem ten problem za pomocą tej metody.

Fogni
źródło
10

Jeśli używasz szybkiego, użyj tego w ten sposób. Nie używaj scenorysu, aby wybrać wysokość wiersza. Programowo ustawiaj wysokość wiersza tabeli w ten sposób,

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 || indexPath.row == 1{
        let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
        self.tableView.rowHeight = 150
        cell.label1.text = "hiiiiii"
        cell.label2.text = "Huiiilllllll"
        return cell

    } else {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
        self.tableView.rowHeight = 60
        cell.label3.text = "llll"
        return cell
    }

}
Chathuranga Silva
źródło
Działa to, ale bardziej ogólnie można:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
Andy Chou,
7

Możesz uzyskać wysokość UITableviewCell (w UITableviewController - komórki statyczne) z serii ujęć za pomocą następujących wierszy.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
   CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];

    return height;
}
Hiren Panchal
źródło
6

Otwórz scenorys w widoku XML i spróbuj edytować rowHeightatrybut pożądanego elementu.

Działa to dla mnie, gdy próbowałem ustawić niestandardową wysokość wiersza dla mojego prototypowanego wiersza. Nie działa przez inspektora, ale działa przez XML.

Shtirlic
źródło
1
Kliknąłem prawym przyciskiem myszy i wybrałem „otwórz jako” -> „kod źródłowy”. RowHeight został już ustawiony na 120. Uważam, że w scenorysie występuje błąd i ignoruje niestandardową wysokość komórki.
zirinisp,
6

Możesz użyć prototypu cellsz niestandardowym height, a następnie wywołać cellForRowAtIndexPath:i zwrócić jego frame.height.:.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}
Henrik Hartz
źródło
Dodanie tej metody działało bardzo dobrze i teraz zachowuje wszystkie dostosowania w serii ujęć tak, jak powinno.
daspianista
Zdecydowanie najlepsze rozwiązanie
TheJeff
4

Jeśli chcesz ustawić statyczną wysokość wiersza, możesz zrobić coś takiego:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 120;
}
Victor Palhares
źródło
4

Ostatnio walczyłem z tym. Mój problem polegał na tym, że powyższe rozwiązania wykorzystujące tę heightForRowAtIndexPath:metodę działałyby na iOS 7.1 w Symulatorze, ale potem całkowicie spieprzyły wyniki, po prostu przechodząc na iOS 8.1.

Zacząłem czytać więcej o samorozmierzających się komórkach (wprowadzonych w iOS 8, czytaj tutaj ). Było oczywiste, że użycie UITableViewAutomaticDimensionpomoże w iOS 8. Próbowałem użyć tej techniki i usunąłem użycie heightForRowAtIndexPath:i voila, teraz działało idealnie w iOS 8. Ale potem iOS 7 nie był. Co miałam zrobić? Potrzebowałem heightForRowAtIndexPath:na iOS 7, a nie na iOS 8.

Oto moje rozwiązanie (przygotowane dla zwięzłości), które pożyczyłem od odpowiedzi @JosephH zamieszczonej powyżej:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 50.;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    // ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        return UITableViewAutomaticDimension;

    } else {
        NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath:indexPath];
        static NSMutableDictionary *heightCache;
        if (!heightCache)
            heightCache = [[NSMutableDictionary alloc] init];
        NSNumber *cachedHeight = heightCache[cellIdentifier];
        if (cachedHeight)
            return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
    }
}

- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
    NSString * reuseIdentifier;
    switch (indexPath.row) {
        case 0:
            reuseIdentifier = EventTitleCellIdentifier;
            break;
        case 2:
            reuseIdentifier = EventDateTimeCellIdentifier;
            break;
        case 4:
            reuseIdentifier = EventContactsCellIdentifier;
            break;
        case 6:
            reuseIdentifier = EventLocationCellIdentifier;
            break;
        case 8:
            reuseIdentifier = NotesCellIdentifier;
            break;
        default:
            reuseIdentifier = SeparatorCellIdentifier;
            break;
    }

    return reuseIdentifier;
}

SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO (@ „8.0”) jest właściwie z zestawu definicji makr, których używam, które gdzieś znalazłem (bardzo pomocne). Są one zdefiniowane jako:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
mówić
źródło
2

Ten sam problem wystąpił podczas pracy na XCode 9 za pomocą Swift 4 .

Dodaj opcję AutoLayout dla elementów interfejsu użytkownika w komórce, a niestandardowa wysokość wiersza komórki będzie działać odpowiednio, jak określono.

Murari Varma
źródło
1
Cześć, proszę, jak to zrobiłeś?
Back Packer
Musisz tylko dodać zestaw ograniczeń na dole komórki.
Justin
Kiedy to zrobię i wybiorę „automatyczny” w serii ujęć dla wysokości komórki, chce ustawić wszystkie wysokości na 44 w serii ujęć, chociaż wydaje mi się, że po uruchomieniu. Jak mogę uzyskać prawidłowe wyświetlanie scenorysu?
David
1

W przypadku komórek dynamicznych wartość rowHeight ustawiona w UITableView zawsze zastępuje wartość rowHeight poszczególnych komórek. Wystarczy obliczyć wysokość dynamiczną, jeśli zawartość wewnątrz wiersza.

Aks
źródło
0

Jedynym prawdziwym rozwiązaniem, jakie mogłem znaleźć, jest to

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
    return cell.frame.size.height;
}

To działa i pozwala nie mieć okropnego przełącznika / jeśli powiela logikę już na StoryBoard. Nie jestem pewien co do wydajności, ale myślę, że po przybyciu cellForRow:do już zainicjowanej komórki jest tak szybko. Oczywiście są tu prawdopodobnie szkody dodatkowe, ale wygląda na to, że działa mi to dobrze.

Opublikowałem to również tutaj: https://devforums.apple.com/message/772464

EDYCJA: Ortwin Gentz przypomniał mi, że heightForRowAtIndexPath:będą wywoływane dla wszystkich komórek TableView, nie tylko widocznych. Brzmi logicznie, ponieważ iOS musi znać całkowitą wysokość, aby móc wyświetlić odpowiednie paski przewijania. Oznacza to, że prawdopodobnie jest w porządku na małych TableViews (jak 20 komórek), ale zapomnij o tym na 1000 TableView.

Również poprzednia sztuczka z XML: tak samo jak pierwszy komentarz dla mnie. Prawidłowa wartość już tam była.

StuFF mc
źródło
0

Dodano jako komentarz, ale opublikowanie jako odpowiedź na widoczność:

Wydaje się również występować problem, jeśli początkowo skonfigurujesz widok tabeli jako „komórki statyczne”, a następnie zmienisz go na „dynamiczne prototypy”. Miałem problem polegający na heightForRowAtIndexPathtym, że nawet metoda delegowania była ignorowana. Najpierw rozwiązałem go, edytując bezpośrednio scenariuszowy kod XML, a na koniec po prostu przebudowałem scenę od podstaw, używając dynamicznych prototypów od samego początku.

Eric Goldberg
źródło
0

Biorąc pod uwagę, że nie znalazłem żadnego rozwiązania tego problemu za pomocą Konstruktora interfejsów, postanowiłem opublikować programowe rozwiązanie problemu w Swift przy użyciu dwóch dynamicznych komórek , mimo że początkowe pytanie dotyczyło rozwiązania za pomocą Konstruktora interfejsów. Niezależnie od tego uważam, że może to być pomocne dla społeczności Stack Overflow:

    import UIKit

    enum SignInUpMenuTableViewControllerCellIdentifier: String {
       case BigButtonCell = "BigButtonCell"
       case LabelCell = "LabelCell"
    }

    class SignInUpMenuTableViewController: UITableViewController {
            let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
                              SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]

    private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
        if indexPath.row == 2 {
            return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
        } else {
            return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
        }
    }

   override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
       return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
   }

   ...

  }
King-Wizard
źródło
Wszystko jest w porządku i dobre, ale chyba że źle czytam kod, po prostu opiera się na magicznych liczbach i całkowicie ignoruje dowolną wartość ustawioną w IB dla komórek dynamicznych, więc ... nie do końca rozwiązanie pytania OP
Danny
0

Inną rzeczą, którą możesz zrobić, to przejść do Zarysu dokumentu, wybrać widok tabeli, w którym zagnieżdżona jest komórka prototypowa. Następnie w Inspektorze rozmiarów zmień widok tabeli Wysokość wiersza na żądaną wartość i odznacz pole Automatyczne.

Amin Rezapour
źródło