Пользовательская настройка высоты строки ячейки в раскадровке не отвечает

213

Я пытаюсь настроить высоту ячейки для одной из ячеек в моем табличном представлении. Я настраиваю размер с помощью параметра «Высота строки» внутри «Инспектора размеров» рассматриваемой ячейки. Когда я запускаю приложение на своем iPhone, размер ячейки по умолчанию устанавливается из «размера строки» в табличном представлении.

Если я изменю «размер строки» табличного представления, то размер всех ячеек изменится. Я не хочу этого делать, поскольку я хочу нестандартный размер только для одной ячейки. Я видел много постов, в которых есть программное решение проблемы, но я бы предпочел сделать это через раскадровку, если это возможно.

zirinisp
источник

Ответы:

295

В динамических ячейках, rowHeightустановленный в UITableView, всегда переопределяет rowHeight отдельных ячеек.

Но для статических ячеек, rowHeightустановленных для отдельных ячеек, можно переопределить UITableView.

Не уверен, что это ошибка, Apple может специально делать это?

pixelfreak
источник
36
Правильный ответ № 3, в частности, потому что этот ответ относится к Интерфейсному Разработчику / Раскадровке. Если выбрать ячейку в IB, то размер Инспектор показывает Высота строки в верхней части (с «обычаем» флажком), но если вы выбираете всю таблицу просмотр в размере Инспектор показывает Высоту строки в верхних там тоже (нет « на заказ " в таком случае). Как говорит pixelfreak, для динамических ячеек используется только настройка табличного представления. (Не уверен, что это преднамеренно)
Ревень
8
этот ответ подразумевает, что решение будет состоять в том, чтобы просто изменить UITableViewсодержание с Dynamic Prototypesна Static Cells, я сделал это, и весь мой проект взорвался ... чуть не убил себя.
Abbood
4
Есть ли способ получить этот номер? Единственный способ углубиться в это - это погрузиться в раскадровку и вытащить ее оттуда?
Biclops
Это определенно НЕ ошибка, потому что ваша таблица динамическая, так как система может узнать, где вы использовали каждую из этих ячеек? И сколько раз будет использоваться каждый прототип.
Винсент Бернье
Кажется также, что проблема заключается в том, что вы изначально устанавливаете табличное представление как «статические ячейки», а затем меняете его на «динамические прототипы». У меня была проблема, когда даже метод делегата для rowHeight игнорировался. Сначала я решил эту проблему путем непосредственного редактирования XML раскадровки, а затем, наконец, просто перестроил сцену раскадровки с нуля, используя динамические прототипы с самого начала.
Эрик Голдберг
84

Если вы используете UITableViewController, реализуйте этот метод:

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

В функции ряда вы можете выбрать Высота. Например,

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

В этом примере высота первой строки составляет 100 пикселей, а остальные - 60 пикселей.

Я надеюсь, что этот может помочь вам.

Beber
источник
2
Бебер, нужно ли ВСЕГДА делать и то, и другое (отметьте «Пользовательский» и отредактируйте значение «Высота строки» в раскадровке и укажите его в heightForRowAtIndexPath)?
Марсиококо
Не могли бы вы помочь мне в этом? Мне нужно иметь динамические ячейки с динамической высотой, но если я использую этот метод, независимо от того, что я возвращаю, все ячейки исчезают. За исключением iPhone 5 устройства , где оно работает как положено. Ты можешь понять почему?
Остмеистро
34

Для динамических ячеек rowHeightзначение UITableViewвсегда переопределяет отдельные ячейки rowHeight.

Это поведение, IMO, ошибка. Каждый раз, когда вам приходится управлять своим пользовательским интерфейсом в двух местах, он подвержен ошибкам. Например, если вы меняете размер ячейки в раскадровке, вы должны помнить, чтобы изменить их heightForRowAtIndexPath:также. До тех пор, пока Apple не исправит ошибку, лучшим решением в настоящее время является переопределение heightForRowAtIndexPath:, но использование фактических ячеек прототипа из раскадровки для определения высоты, а не использование магических чисел . Вот пример:

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

Это обеспечит автоматическое отслеживание любых изменений высоты прототипа во время выполнения, и вам нужно будет управлять своим пользовательским интерфейсом только в одном месте: раскадровке.

memmons
источник
2
Я отправил ответ с полным кодом, включая кеширование; см. stackoverflow.com/a/16881312/292166
JosephH
1
Вау вау вау! Я проголосовал за ответ, но будьте осторожны! Этот метод вызывает не только снижение производительности, как сказал @Brennan в комментариях, он также вызывает растущее выделение памяти при каждом reloadData, что-то вроде утечки памяти! Нужно использовать обходной путь Ленсовета выше! Потратьте день, чтобы поймать эту утечку памяти!
Skywinder
Не работает в Swift, потому что cell.bounds.size.height всегда возвращает 0.0
King-Wizard
1
Ячейки еще не существуют в тот момент, когда система вызывает ваш метод tableView: heightForRowAtIndexPath. Предполагается, что вы сможете использовать indexPath, который передается вам, для индексации в вашей модели данных и посмотреть, какая ячейка будет использоваться там, а затем вычислить высоту для этой ячейки и вернуть ее. Это позволяет системе размещать ячейки в таблице до того, как она создаст какие-либо ячейки.
Король-волшебник
1
Кроме того, вы не должны вызывать свой собственный метод cellForRowAtIndexPath. Табличные представления имеют метод cellForRowAtIndexPath, который возвращает ячейку для этого пути индекса, если он в данный момент виден на экране, но часто этот путь индекса не имеет связанной с ним ячейки.
Король-волшебник
22

Я создал код, на который намекают различные ответы / комментарии, чтобы он работал для раскадровок, использующих ячейки прототипа.

Этот код:

  • Не требует, чтобы высота ячейки была установлена ​​где-либо кроме очевидного места в раскадровке
  • Кэширует высоту по соображениям производительности
  • Использует общую функцию для получения идентификатора ячейки для пути индекса, чтобы избежать дублирования логики

Спасибо Ответботу, Бреннану и Ленсовету.

- (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
источник
Я знаю, что это старый ответ, но кто-нибудь еще находит, что это приводит к переполнению стека? Когда представление загружается, я получаю UISectionRowData refreshWithSection: tableViewRowData: который вызывает tableView: heightForRowAtIndexPath: который вызывает dequeueReusableCellWithIdentifier: который вызывает [UISectionRowData ...], поэтому у меня рекурсивное переполнение стека. Я действительно хотел использовать это решение, но, похоже, оно не работает с iOS7.
sbaker
Не работает в Swift, потому что cell.bounds.size.height всегда возвращает 0.0
King-Wizard
Ячейки еще не существуют в тот момент, когда система вызывает ваш метод tableView: heightForRowAtIndexPath. Предполагается, что вы сможете использовать indexPath, который передается вам, для индексации в вашей модели данных и посмотреть, какая ячейка будет использоваться там, а затем вычислить высоту для этой ячейки и вернуть ее. Это позволяет системе размещать ячейки в таблице до того, как она создаст какие-либо ячейки.
Король-волшебник
Кроме того, вы не должны вызывать свой собственный метод cellForRowAtIndexPath. Табличные представления имеют метод cellForRowAtIndexPath, который возвращает ячейку для этого пути индекса, если он в данный момент виден на экране, но часто этот путь индекса не имеет связанной с ним ячейки.
Король-волшебник
@ snow-tiger Я не пробовал это быстро, но я не понимаю ваши комментарии. Я не «вызываю [мой] собственный метод cellForRowAtIndexPath», и я намеренно этого не делаю. Я также знаю, что ячейки не существуют в то время, когда вызывается tableView: heightForRowAtIndexPath, в этом весь смысл этого кода и почему он использует dequeueReusableCellWithIdentifier для получения ячейки. Код, который размещен в этом ответе, работает. Либо в swift происходит что-то дополнительное, либо что-то не так в быстром преобразовании, либо вы пытаетесь решить другую проблему, на которую нацелен этот вопрос / ответ.
Джозеф
19

На самом деле есть два места, где вам нужно изменить высоту строки: сначала ячейка (вы уже это изменили), а затем выберите представление таблицы и проверьте инспектор размера.

Кристьян Х.
источник
в виде таблицы (не в ячейке)> вкладка инспектора по размеру> высота строки
iman kazemayni
Сводит меня с ума каждый раз, когда я забываю эту деталь. Не знаю, почему настройка ячейки не обновляет tableView автоматически при использовании раскадровок!
Скутер
11

Я думаю, что это ошибка.

Попробуйте отрегулировать высоту не инспектором утилит, а перетаскиванием мышью непосредственно по раскадровке.

Я решил эту проблему с помощью этого метода.

Fogni
источник
10

Если вы используете Swift, используйте как это. Не используйте раскадровку, чтобы выбрать высоту строки. Программно установить высоту строки таблицы следующим образом:

 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
    }

}
Чатуранга Силва
источник
Это работает, но в целом вы можете сделать:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
Энди Чоу
7

Вы можете получить высоту UITableviewCell (в UITableviewController - статические ячейки) из раскадровки с помощью следующих строк.

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

    return height;
}
Хирен Панчал
источник
6

Откройте раскадровку в представлении XML и попробуйте изменить rowHeightатрибут требуемого элемента.

Это сработало для меня, когда я попытался установить собственный rowHeight для моей прототипированной строки. Это не работает через инспектора, но через XML это работает.

Shtirlic
источник
1
Я щелкнул правой кнопкой мыши и выбрал «открыть как» -> «исходный код». Значение rowHeight уже было установлено равным 120. Я полагаю, что в раскадровке есть ошибка, и она игнорирует настраиваемую высоту ячейки.
zirinisp
6

Вы можете использовать прототип cellsс кастомом height, а затем вызывать cellForRowAtIndexPath:и возвращать его frame.height.:.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}
Хенрик Харц
источник
Добавление этого метода сработало очень хорошо, и теперь сохраняет все настройки в раскадровке, как и должно быть.
daspianist
Безусловно лучшее решение
TheJeff
4

Если вы хотите установить статическую высоту строки, вы можете сделать что-то вроде этого:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 120;
}
Виктор Палхарес
источник
4

Я недавно боролся с этим. Моя проблема заключалась в том, что рассмотренные выше решения с использованием этого heightForRowAtIndexPath:метода работали бы для iOS 7.1 в Симуляторе, но затем полностью облажались, просто переключившись на iOS 8.1.

Я начал читать больше о самоконтроля ячеек (введен в iOS 8, читайте здесь ). Было очевидно, что использование UITableViewAutomaticDimensioniOS помогло бы в iOS 8. Я попытался использовать эту технику и удалил использование heightForRowAtIndexPath:и вуаля, теперь она отлично работает в iOS 8. Но тогда iOS 7 не было. Что мне было делать? Мне нужно было heightForRowAtIndexPath:для iOS 7, а не для iOS 8.

Вот мое решение (сокращенное для краткости), которое позаимствовано из ответа @JosephH, опубликованного выше:

- (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") на самом деле из набора определений макросов, которые я использую, которые я где-то нашел (очень полезно). Они определены как:

#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)
speby
источник
2

Та же проблема возникла при работе на XCode 9 с использованием Swift 4 .

Добавьте AutoLayout для элементов пользовательского интерфейса внутри ячейки, и высота строки пользовательской ячейки будет работать соответственно, как указано.

Мурари Варма
источник
1
Привет, пожалуйста, как ты это сделал?
Back Packer
Вам просто нужно добавить набор ограничений в нижней части ячейки.
Джастин
Когда я делаю это и выбираю «автоматический» в раскадровке для высоты ячейки, он хочет установить для всех высот значение 44 в раскадровке, хотя это выглядит правильно, когда я бегу. Как я могу заставить раскадровку отображаться правильно?
Дэвид
1

Для динамических ячеек rowHeight, установленный в UITableView, всегда переопределяет rowHeight отдельных ячеек. Просто рассчитайте динамическую высоту, если содержимое внутри строки.

Акс
источник
0

Единственное реальное решение, которое я смог найти, это

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

Это работает и позволяет не иметь ужасного переключателя / при дублировании логики уже в StoryBoard. Не уверен насчет производительности, но я думаю, что когда вы cellForRow:попадаете в камеру, которая уже инициализирована, это так же быстро. Конечно, здесь, возможно, есть побочные убытки, но, похоже, у меня это работает нормально.

Я также разместил это здесь: https://devforums.apple.com/message/772464

РЕДАКТИРОВАТЬ: Ортвин Генц напомнил мне, что heightForRowAtIndexPath:будет вызываться для всех ячеек TableView, а не только видимых. Звучит логично, поскольку iOS должна знать общую высоту, чтобы можно было показывать правильные полосы прокрутки. Это означает, что это возможно для небольших TableViews (например, 20 ячеек), но забудьте об этом в 1000 Cell TableView.

Кроме того, предыдущий трюк с XML: То же, что первый комментарий для меня. Правильное значение уже было там.

StuFF mc
источник
0

Добавлен как комментарий, но размещен как ответ для наглядности:

Кажется также, что проблема заключается в том, что вы изначально устанавливаете табличное представление как «статические ячейки», а затем меняете его на «динамические прототипы». У меня была проблема, когда даже метод делегата heightForRowAtIndexPathигнорировался. Сначала я решил эту проблему путем непосредственного редактирования XML раскадровки, а затем, наконец, просто перестроил сцену раскадровки с нуля, используя динамические прототипы с самого начала.

Эрик Голдберг
источник
0

Учитывая, что я не нашел решения этой проблемы с помощью Interface Builder, я решил опубликовать программное решение проблемы в Swift с использованием двух динамических ячеек , хотя первоначальный вопрос задавался для решения с помощью Interface Builder. Несмотря на это, я думаю, что это может быть полезно для сообщества 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)]!
   }

   ...

  }
Король-Мастер
источник
Это все хорошо, но если я неправильно читаю код, он просто полагается на магические числа и полностью игнорирует любое значение, которое вы устанавливаете в IB для динамических ячеек, так что ... не совсем решение вопроса OP
Дэнни
0

Еще одна вещь, которую вы можете сделать, это перейти к вашей структуре документа, выбрать табличное представление, в которое вложена ячейка вашего прототипа. Затем в инспекторе размеров измените в табличном представлении высоту строки нужное значение и снимите флажок «Автоматически».

Амин Резапур
источник