Получить ссылку на ячейку под 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
.
Обратите внимание, так как количество строк и высоты в этом демонстрационном проекте абсолютно случайное, это будет перезаряжаться и мерцать каждый раз, когда ячейка будет повторно использоваться. Не должно происходить реальных данных.