22

ストーリーボードのコンテンツを指定するために「動的プロトタイプ」を使用する場合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
4

2 に答える 2

28

静的な(データ駆動型ではない)高さの場合、セルを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];
}

動的な(データ駆動型)高さの場合、プロトタイプセルをビューコントローラーに格納し、サブビューの配置、フォントなどのプロトタイプインスタンスのデフォルトのコンテンツを考慮して、高さを計算するメソッドをセルのクラスに追加できます。等。:

- (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:。このアプローチは、デザイナーがコードを変更せずにストーリーボードのセルレイアウトを変更できるため、非常にうまく機能しました。

編集:サンプルコードの意味を明確にし、静的な高さの場合の例を追加しました。

于 2013-01-02T19:28:37.707 に答える
5

少し前に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];
}
于 2013-02-20T15:09:10.910 に答える