2012-12-01 3 views
22

При использовании «Динамических прототипов» для указания содержимого UITableView на раскадровке существует свойство «Высота строки», которое может быть установлено на «Пользовательский».Извлечение пользовательской высоты прототипа из раскадровки?

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

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

[myTableView heightForCellWithReuseIdentifier:@"MyCellPrototype"]; 

или что-то в этом направлении? Или мне нужно дублировать явные высоты строк в моем коде приложения, с последующим бременем обслуживания?

решаемые с помощью @TimothyMoose:

Высоты хранятся в самих клеток, а это значит, единственный способ получения высот является экземпляр прототипов. Один из способов сделать это - предварительно очистить ячейки вне обычного метода обратного вызова. Вот мой маленький POC, который работает:

#import "ViewController.h" 

@interface ViewController() { 
    NSDictionary* heights; 
} 
@end 

@implementation ViewController 

- (NSString*) _reusableIdentifierForIndexPath:(NSIndexPath *)indexPath 
{ 
    return [NSString stringWithFormat:@"C%d", indexPath.row]; 
} 

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if(!heights) { 
     NSMutableDictionary* hts = [NSMutableDictionary dictionary]; 
     for(NSString* reusableIdentifier in [NSArray arrayWithObjects:@"C0", @"C1", @"C2", nil]) { 
      CGFloat height = [[tableView dequeueReusableCellWithIdentifier:reusableIdentifier] bounds].size.height; 
      hts[reusableIdentifier] = [NSNumber numberWithFloat:height]; 
     } 
     heights = [hts copy]; 
    } 
    NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath]; 
    return [heights[prototype] floatValue]; 
} 

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

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return 1; 
} 

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath]; 
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:prototype]; 
    return cell; 
} 

@end 
+0

Я использовал этот метод некоторое время, а затем на новом раскадровке я получал в основном размеры нулевой высоты (и ширины) для ячеек. Даже ненулевые были неожиданными значениями. Отключение классов размера восстановило эту функциональность. Разочарование, тем не менее, поскольку это «исправление» отключает важную функциональность в раскадровке. Однако это сработало для моей неотложной необходимости. –

+0

Я нашел более полное решение для [решения этой проблемы с включенными классами размера] (http://stackoverflow.com/questions/27735341/offscreen-uitableviewcells-for-size-calculations-not-respecting-size-class). –

ответ

28

Для статической (не управляемых данных) высоты, вы можете просто из очереди в клетке один раз и сохранить высоту:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSNumber *height; 
    if (!height) { 
     UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"]; 
     height = @(cell.bounds.size.height); 
    } 
    return [height floatValue]; 
} 

Для динамического (данных -driven), вы можете сохранить ячейку прототипа в контроллере представления и добавить метод к классу ячейки, который вычисляет высоту, принимая во внимание содержимое по умолчанию экземпляра прототипа, такое как размещение в режиме просмотра, шрифты и т. д .:

- (MyCustomCell *)prototypeCell 
{ 
    if (!_prototypeCell) { 
     _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"]; 
    } 
    return _prototypeCell; 
} 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // Data for the cell, e.g. text for label 
    id myData = [self myDataForIndexPath:indexPath]; 

    // Prototype knows how to calculate its height for the given data 
    return [self.prototypeCell myHeightForData:myData]; 
} 

Конечно, если вы используете пользовательскую высоту, у вас, вероятно, есть несколько прототипов ячеек, поэтому вы можете хранить их в словаре или что-то в этом роде.

Насколько я могу судить, в представлении таблицы не делается попытки повторного использования прототипа, по-видимому, потому, что он был удален из-за cellForRowAtIndexPath:. Этот подход очень сработал для нас, потому что он позволяет дизайнеру изменять макеты ячеек в раскадровке без каких-либо изменений кода.

Редактировать: разъяснил смысл примера кода и добавил пример для случая статической высоты.

+0

Я не признаю метод 'heightForData:'. Что оно делает? – Krumelur

+0

'heightForData:' - метод, который вы должны писать для вычисления высоты для данных данной ячейки. –

+0

Я вижу. Проблема в том, что я хочу использовать высоту, сохраненную в самом файле раскадровки (статическая высота). Ваш pre-dequeueing клеток может работать для этого тоже. Я это попробую. – Krumelur

5

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

UITableView + PrototypeCells.h

#import <UIKit/UIKit.h> 

@interface UITableView (PrototypeCells) 

- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier; 
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier; 

@end 

UITableView + PrototypeCells.м

#import "UITableView+PrototypeCells.h" 
#import <objc/runtime.h> 

static char const * const key = "prototypeCells"; 

@implementation UITableView (PrototypeCells) 
- (void)setPrototypeCells:(NSMutableDictionary *)prototypeCells { 
    objc_setAssociatedObject(self, key, prototypeCells, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 

- (NSMutableDictionary *)prototypeCells { 
    return objc_getAssociatedObject(self, key); 
} 

- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier { 
    return [self prototypeCellWithReuseIdentifier:reuseIdentifier].frame.size.height; 
} 

- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier { 
    if (self.prototypeCells == nil) { 
     self.prototypeCells = [[NSMutableDictionary alloc] init]; 
    } 

    UITableViewCell* cell = self.prototypeCells[reuseIdentifier]; 
    if (cell == nil) { 
     cell = [self dequeueReusableCellWithIdentifier:reuseIdentifier]; 
     self.prototypeCells[reuseIdentifier] = cell; 
    } 
    return cell; 
} 

@end 

Использование

Получение статической высоты, установленная в раскадровке так же просто, как это:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    return [tableView heightForRowWithReuseIdentifier:@"cellIdentifier"]; 
} 

Предполагая многосекционных вида таблицы:

enum { 
    kFirstSection = 0, 
    kSecondSection 
}; 

static NSString* const kFirstSectionRowId = @"section1Id"; 
static NSString* const kSecondSectionRowId = @"section2Id"; 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    CGFloat height = tableView.rowHeight; // Default UITableView row height 
    switch (indexPath.section) { 
     case kFirstSection: 
      height = [tableView heightForRowWithReuseIdentifier:kFirstSectionRowId]; 
      break; 
     case kSecondSection: 
      height = [tableView heightForRowWithReuseIdentifier:kSecondSectionRowId]; 
    } 
    return height; 
} 

И, наконец, если высота строки динамическая:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    id thisRowData = self.allData[indexPath.row]; // Obtain the data for this row 

    // Obtain the prototype cell 
    MyTableViewCell* cell = (MyTableViewCell*)[self prototypeCellWithReuseIdentifier:@"cellIdentifier"]; 

    // Ask the prototype cell for its own height when showing the specified data 
    return [cell heightForData:thisRowData]; 
} 
+0

Это выглядит действительно полезно. Благодаря! – Krumelur

+0

Отличная реализация, вы должны положить это на github & pot –

Смежные вопросы