ストーリーボードでUITableView
コンテンツを指定するために「動的プロトタイプ」を使用する場合、カスタムに設定できる「行の高さ」プロパティがあります。
セルをインスタンス化するとき、このカスタム行の高さは考慮されません。使用するプロトタイプセルは、セルをインスタンス化するときのアプリケーションコードによって決定されるため、これは理にかなっています。レイアウトを計算するときにすべてのセルをインスタンス化すると、パフォーマンスが低下するため、それができない理由を理解しています。
次に、セル再利用識別子を指定して高さを取得できますか?.
[myTableView heightForCellWithReuseIdentifier:@"MyCellPrototype"];
またはその線に沿って何か?または、アプリケーションコードで明示的な行の高さを複製する必要がありますが、それに続くメンテナンスの負担がありますか?
@ TimothyMooseの助けを借りて解決:
高さはセル自体に保存されます。つまり、高さを取得する唯一の方法は、プロトタイプをインスタンス化することです。これを行う1つの方法は、通常のセルコールバックメソッドの外部でセルを事前にデキューすることです。これが私の小さな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
静的な(データ駆動型ではない)高さの場合、セルを1回デキューして、高さを格納できます。
- (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];
}
動的な(データ駆動型)高さの場合、プロトタイプセルをView Controllerに格納し、サブビューの配置、フォントなど、プロトタイプインスタンスのデフォルトのコンテンツを考慮して、高さを計算するメソッドをセルのクラスに追加できます。等。:
- (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:
の外部でキューから取り出されたためです。このアプローチは、設計者がコードを変更せずにストーリーボードのセルレイアウトを変更できるため、非常にうまく機能しました。
編集:サンプルコードの意味を明確にし、静的な高さの場合の例を追加しました。
少し前にUITableViewのカテゴリを作成しましたが、これが役立つ可能性があります。プロトタイプを再利用するために 関連付けられたオブジェクト を使用して「プロトタイプ」セルを格納し、ストーリーボードで割り当てられた行の高さを取得するための便利な方法を提供します。プロトタイプは、テーブルビューの割り当てが解除されると解放されます。
UITableView + ProtocolCells.h
#import <UIKit/UIKit.h>
@interface UITableView (PrototypeCells)
- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier;
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier;
@end
UITableView + ProtocolCells.m
#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];
}