24/7 twenty-four seven

iOS/OS X application programing topics.

Interface Builderを使ってカスタマイズしたUITableViewCellを作る方法(追記あり)

[追記]大事な手順を書き忘れていました。セルの再利用のための手順を最後に追記しています。


間違ってるかもしれないけど、たぶんこう。


View Templateを選択して、新しいXIBファイルを作成します。

もともとのViewを削除します。

代わりにUITableViewCellを配置します。

適当にセルをデザインします。

名前を付けて保存します。ここではFeedListCell.xibとします。


Xcodeで対応するUITableViewCellを継承したクラスと、View Controller (FeedListCellController) を作ります。
View ControllerはUIViewControllerを継承します(UITableViewControllerではない)。
また、セルをインスタンス変数として保持します。ここではFeedListCellのインスタンスです。

#import <UIKit/UIKit.h>

@interface FeedListCell : UITableViewCell {
	IBOutlet UILabel *titleLabel;
	IBOutlet UILabel *unreadCountLabel;
	IBOutlet UILabel *readCountLabel;
	IBOutlet UIImageView *unreadMark;
}

@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *unreadCountLabel;
@property (nonatomic, retain) UILabel *readCountLabel;
@property (nonatomic, retain) UIImageView *unreadMark;

@end
#import "FeedListCell.h"

@implementation FeedListCell

@synthesize titleLabel;
@synthesize unreadCountLabel;
@synthesize readCountLabel;
@synthesize unreadMark;

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
	[super setSelected:selected animated:animated];
}

- (void)dealloc {
	[unreadMark release];
	[readCountLabel release];
	[unreadCountLabel release];
	[titleLabel release];
	[super dealloc];
}

@end
#import <UIKit/UIKit.h>
#import "FeedListCell.h"


@interface FeedListCellController : UIViewController {
	IBOutlet FeedListCell *cell;
}

@property (nonatomic, retain) FeedListCell *cell;

@end

Interface Builderに戻って、FeedListCell.xibのFile's OwnerにFeedListCellController、UITableViewCellにFeedListCellを設定します。

View ControllerのセルのインスタンスとViewのインスタンスをセルに接続します。
FeedListCellControllerのcell変数と、view変数を両方ともFeedListCellに接続します。

FeedListCellのインスタンス変数にセルの部品を接続します。

これで、セルの準備は完了です。
実際にTable Viewで表示する場合は次のように記述します。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	static NSString *identifier = @"FeedListCell";
	FeedListCell *cell = (FeedListCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
	if (cell == nil) {
		FeedListCellController *controller = [[FeedListCellController alloc] initWithNibName:identifier bundle:nil];
		cell = (FeedListCell *)controller.view;
		[controller release];
	}
	
	[cell.titleLabel setText:[NSString decodeXMLCharactersIn:[feed objectForKey:@"title"]]];
	[cell.unreadMark setImage:unreadMark2];
	[cell.readCountLabel setText:[NSString stringWithFormat:@"%d", [unread_count intValue] - [listOfReadEachEntry count]]];
	[cell.unreadCountLabel setText:[NSString stringWithFormat:@"%@", unread_count]];
	
	return cell;
}

注意

セルにテキストフィールドなど (UITextField, UITextView) を含める場合は、解放されるときにデリゲートを解放するのを忘れないようにします。
デリゲートが残ったままセルが解放されると、クラッシュします。

#import "DiaryCell.h"

@implementation DiaryCell

@synthesize diaryTextView;
@synthesize submitButton;
@synthesize draftButton;

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
	[super setSelected:selected animated:animated];
}

- (void)dealloc {
	[draftButton release];
	[submitButton release];
	[diaryTextView setDelegate:nil];
	[diaryTextView release];
	[super dealloc];
}

@end

以下の部分です。

	[diaryTextView setDelegate:nil];
	[diaryTextView release];

[追記]セルのインスタンスを再利用するために必要な手順

UITableViewCellのインスタンスは、最初に一画面分のインスタンスを作ったら、後は同じインスタンスを再利用します。
そうしないと、スクロールするたびに新しいインスタンスが作られるので、メモリが足りなくなります。

以下はその再利用のためのコードです。セルのインスタンスに名前を付けて、保持します。
名前を指定して呼び出し、存在しなかった場合だけ(つまり最初の一回だけ)、新しくインスタンスを生成します。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	static NSString *identifier = @"FeedListCell";
	FeedListCell *cell = (FeedListCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
	if (cell == nil) {
		FeedListCellController *controller = [[FeedListCellController alloc] initWithNibName:identifier bundle:nil];
		cell = (FeedListCell *)controller.view;
		[controller release];
	}

そういう仕組みなので、Interface Builderで作成したセルは再利用するために名前をつける必要があります。
セルのファイル (FeedListCell.xib) を開き、UITableViewCellを選択して、「Attributes Inspector(コマンド+1)」を開きます。
TableViewCell>Identifierに任意の名前を設定します。
もちろん、ここで設定した値をコードから使用します。