Получить ссылку на ячейку под heightForRowAtIndexPath: в iOS 8

В настоящее время я работаю над проектом, в который я встроил UITableView внутри UITableViewCell.

Что мне нужно сделать, так это отключить прокрутку UITableView и сделать UITableView в соответствии с размером всех строк. Но поскольку UITableView наследует от UIScrollView, использование Autolayout не заставляет UITableView делать высоту ячейки в зависимости от ее contentSize (а не фрейма) при возврате UITableViewAutomaticDimension.

Решение iOS 7

Это было легко достижимо до iOS 7, поскольку я получаю ссылку на ячейку под heightForRowAtIndexPath:, используя следующий код:

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
int height = cell.tableView.contentSize.height;

return height;

но в iOS 8 он дает BAD_ACCESS, поскольку iOS 8 вызывает heightForRowAtIndexPath: до того, как был вызван cellForRowAtIndexPath:.

Подход iOS 8

Объявить свойство для сохранения ссылки на ячейку:

@property (strong, nonatomic) UITableViewCell *prototypeCell

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

- (id)prototypeCellatIndexPath:(NSIndexPath *)indexPath {
    NSString *cellID = @"MyCell";

    if (!_prototypeCell) {
        _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:cellID];
    }

    return _prototypeCell;
}

Получите UITableView из UITableViewCell из прототипа и из его contentSize. Я получаю высоту, и я возвращаю ее в heighForRowAtIndexPath: из приведенного ниже метода:

-(int)heightForThreadAtIndexPath:(NSIndexPath *)indexPath {
    _prototypeCell = [self prototypeCellatIndexPath:indexPath];

    [_prototypeCell.contentView setNeedsLayout];
    [_prototypeCell.contentView layoutIfNeeded];

    int footer = [_prototypeCell.tableView numberOfSections]*_prototypeCell.tableView.sectionFooterHeight;
    int header = [_prototypeCell.tableView numberOfSections]*_prototypeCell.tableView.sectionHeaderHeight;

    int height = ceilf(_prototypeCell.tableView.contentSize.height) + _prototypeCell.tableView.contentOffset.y + _prototypeCell.tableView.contentInset.bottom + _prototypeCell.tableView.contentInset.top + header + footer;
    NSLog(@"%i, %i", (int)ceilf(_prototypeCell.tableView.contentSize.height), height);

    return height;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [self heightForThreadAtIndexPath:indexPath];
}

Проблема

contentSize.height, который я возвращаю из prototypeCell, неверен, и он не соответствует реальному contentSize UITableView, но когда я регистрирую реальный contentSize под Класс CustomCell показывает правильный контент, который отличается от того, который находится под прототипомCell.

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

Я много разбираюсь и пробую разные идеи, но пока никто не работал. Я не знаю, пытался ли кто-то попытаться достичь подобной вещи, как я, и решил это. Будет очень приятно, если вы дадите мне идею или что-то в этом роде.

Ответы

Ответ 1

Как вы сказали, метод делегата heightForRowAtIndexPath не даст вам динамическую высоту строк при автоматическом вызове. Вместо этого вы должны явно называть его как: [self delegateMethod] т.е. [self tableView:tableView cellForRowAtIndexPath:indexPath];

Если ваш основной tableView объявлен как IBOutlet как myTableView, тогда даже вызов [self.myTableView cellForRowAtIndexPath:indexPath] не будет работать!!

Я тестировал код, и это работает для меня:

Внутри MainTableViewController.m:

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
  return 7;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"tableViewCellMain"];
  if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"tableViewCellMain"];
  }

  UITableView *tableViewChild = [[UITableView alloc] initWithFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.y, tableView.frame.size.width, tableView.frame.size.height) style:UITableViewStylePlain];
  [self.cls setNumberOfRows:indexPath.row+1];
  [tableViewChild setDelegate:self.cls];
  [tableViewChild setDataSource:self.cls];
  [tableViewChild setSeparatorStyle:UITableViewCellSeparatorStyleNone];
  [tableViewChild setScrollEnabled:NO];
  [tableViewChild reloadData];
  [cell addSubview:tableViewChild];
  return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
  CGFloat height = cell.frame.size.height;
  for (int i=0; i<cell.subviews.count; i++) {
    UITableView *childTableView = (UITableView*) [cell.subviews lastObject];
    height = childTableView.contentSize.height;
  }

  NSLog(@"%f",height);
  return height;
}

Я установил другой класс в качестве делегата для childTableView, чтобы получить его данные.

Внутри ChildTableViewController.m:

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
  return self.numberOfRows;
}

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"tableViewCellChild"];
  if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"tableViewCellChild"];
  }
  [cell.textLabel setText:[NSString stringWithFormat:@"%ld",indexPath.row]];
  UIColor *cellTextColor = [UIColor blackColor];

  switch (indexPath.row)
  {
    case 0: cellTextColor = [UIColor redColor]; break;
    case 1: cellTextColor = [UIColor greenColor]; break;
    case 2: cellTextColor = [UIColor blueColor]; break;
    case 3: cellTextColor = [UIColor magentaColor]; break;
    case 4: cellTextColor = [UIColor purpleColor]; break;
    default: break;
  }
  [cell.textLabel setTextColor:cellTextColor];
  [tableView sizeToFit];
  return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
  return 30;
}

И вы получите работу, как показано на этом изображении:

UITableView внутри строк UITableView

Моя раскадровка выглядит так:

введите описание изображения здесь

Вы можете использовать любой способ создания динамического содержимого для mainTableView, а использование другого класса для childTableView не требуется.

Ответ 2

Вы можете использовать свой подход iOS 7 также в iOS 8, единственное, что изменилось, вы не можете использовать delegate из вашего метода heightForRowAtIndexPath:, но вызывать фактический cellForRowAtIndexPath: в вашем контроллере, поэтому измените строку:

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

в строку:

UITableViewCell* cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];

И должен работать.

Ответ 3

Я не уверен, что это поможет вам или нет, но я должен был сделать что-то подобное раньше, и я заканчиваю его до встроенного UICollectionView с вертикальной прокруткой в ​​ячейке, поэтому он действует как tableview, но другой вид не UITableView, и это делает я могу контролировать каждый отдельно от другого

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

Ответ 4

Я установил другой ответ, я полностью установил динамику и не могу воспроизвести вашу проблему: Вот репозиторий: https://bitbucket.org/Kettu/dynamiccellfrorow. То, что я делаю, - это перезагрузить данные вызова, когда я awakeForNib, который вычисляет все строки в мухе, и конечное значение может быть найдено, как в iOS 7 под scrollView contentSize.

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