[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
UITableViewControllerはどこへ行った?
UIViewControllerのアイコンだけがそのまま残されているため、ぱっと見で今までの選択用アイコンがインストールに失敗して消えてしまったかのように思えます。私はしばらくの間これで大丈夫なのかと不安になりました。でもこれでは別に異常ではなく、正常な状態のようです。ではUITableViewController subclassやUITableViewCell subclassなどはどこに行ってしまったのでしょうか。
iPhone OS 3.0 におけるセルの変更点
左にある画像が4種類のスタイルをそれぞれ適応したセルです。一番上がUITableViewCellStyleDefaultです。textLabelだけ表示される、今までに近い形の表示です。次がUITableViewCellStyleValue1です。タイトルと値を表示しますが、セルの幅を全て使う表示になります。その次がUITableViewCellStyleValue2です。タイトルと値が寄せて表示されます。最後がUITableViewCellStyleSubtitleです。2つのラベルが上下に並んで表示されます。ちなみに画像もこれまでは「セルの変数名.image」でしたが、「セルの変数名.imageView.image」となります。画像はスタイルがUITableViewCellStyleDefaultとUITableViewCellStyleSubtitleの場合だけラベルの左に挿入されます。
以下は上のスクリーンショットを撮影するのに使ったコードです。Navigation-based Applicationを選んでRootViewController.mの該当箇所をこのコードのように書き換えれば同じように表示されるはずです。Xcodeにそのままコピー&ペーストできるようにレイアウトはいじっていません。なお、これだけ少数のセルなので表示は全く問題ないはずですが、一応複数種のセルを使っていることもあるので、以前の記事『数種類のセルを使用してもスクロール速度の低下を極力減らす方法』と同じ書き方にしてあります。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 4;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 行ごとに表示を変更します。
switch (indexPath.row) {
case 0: {
UITableViewCell *cellDefault = [tableView dequeueReusableCellWithIdentifier:@"cellDefault"];
if (cellDefault == nil) {
cellDefault = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellDefault"] autorelease];
}
cellDefault.textLabel.text = @"textLabel";
return cellDefault;
}
break;
case 1: {
UITableViewCell *cellValue1 = [tableView dequeueReusableCellWithIdentifier:@"cellValue1"];
if (cellValue1 == nil) {
cellValue1 = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"cellValue1"] autorelease];
}
cellValue1.textLabel.text = @"textLabel";
cellValue1.detailTextLabel.text = @"detailTextLabel";
return cellValue1;
}
break;
case 2: {
UITableViewCell *cellValue2 = [tableView dequeueReusableCellWithIdentifier:@"cellValue2"];
if (cellValue2 == nil) {
cellValue2 = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:@"cellValue2"] autorelease];
}
cellValue2.textLabel.text = @"textLabel";
cellValue2.detailTextLabel.text = @"detailTextLabel";
return cellValue2;
}
break;
case 3: {
UITableViewCell *cellSubtitle = [tableView dequeueReusableCellWithIdentifier:@"cellSubtitle"];
if (cellSubtitle == nil) {
cellSubtitle = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cellSubtitle"] autorelease];
}
cellSubtitle.textLabel.text = @"textLabel";
cellSubtitle.detailTextLabel.text = @"detailTextLabel";
return cellSubtitle;
}
break;
}
UITableViewCell *cellDefault = [tableView dequeueReusableCellWithIdentifier:@"cellDefault"];
if (cellDefault == nil) {
cellDefault = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellDefault"] autorelease];
}
return cellDefault;
}
2010年5月23日補足:
最後の実際に使われない部分はreturn nil;でもセル数との整合性が失われていなければ全く問題ないのですが、例えばtableView:numberOfRoawsInSection:で戻る値を4から5に変更するとここで空のオブジェクトが返り、結果としてアプリケーションが強制終了されてしまいます。そのような場合でも一応セルオブジェクトを返す事によりアプリケーション全体が落ちるのを避けられるようにしているため、このような一見すると面倒な表記になっています。
数種類のセルを使用してもスクロール速度の低下を極力減らす方法
最近Tiny3Dで各基本図形の詳細設定をする画面のテーブルスクロールを少しでも高速にしようといろいろ試行錯誤していてわかったのですが、セルの再利用を行えるかの判定部分の処理はかなり重たいようです。スクロール時には画面に表示するセルが次々変化するため、その度に行われるセル再利用判定がスクロールの邪魔をしてしまっていたのです。
つまり、それらの判定を必要最小限に行うようにした方がテーブルのスクロールを高速に出来ます。そのためには、たとえ複数回の重複部分が発生してコードの行数が多くなろうと、セルのセクションや行での分岐後で、そのとき必要なセルに限定した再利用判定をした方が良いということになります。以前のコードをテーブルのスクロール速度も考慮したものに変更すると、以下のように書き換えられます。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: {
HorizontalSwitchTableViewCell *sCell =
(HorizontalSwitchTableViewCell *)[tableView
dequeueReusableCellWithIdentifier:@"HorizontalSwitchTableViewCell"];
if (sCell == nil) {
sCell = [[[HorizontalSwitchTableViewCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:@"HorizontalSwitchTableViewCell"]autorelease];
}
sCell.selectionStyle = UITableViewCellSelectionStyleNone;
sCell.myDictionary = self.myDictionary;
sCell.editedKey = @"display";
[sCell.switchCtl setOn:[[myDictionaryobjectForKey:@"display"]boolValue]
animated:NO];
sCell.nomalLabel.text = NSLocalizedString(@"Display",@"");
return sCell;
break;
}
case 1: {
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier:@"Cell"] autorelease];
}
cell.font = [UIFont systemFontOfSize:20];
cell.text = [NSString stringWithFormat:NSLocalizedString(@"Name
: %@",@""),[myDictionary objectForKey:@"name"]];
return cell;
break;
}
case 2: {
HorizontalPositionTableViewCell *pCell =
(HorizontalPositionTableViewCell *)[tableView
dequeueReusableCellWithIdentifier:@"HorizontalPositionTableViewCell"];
if (pCell == nil) {
pCell = [[[HorizontalPositionTableViewCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:@"HorizontalPositionTableViewCell"] autorelease];
}
pCell.enabledSetting = YES;
pCell.titleLabel.text = NSLocalizedString(@"Center",@"");
pCell.leftValueLabel.text = [NSStringstringWithFormat:@"%f",[[myDictionary
objectForKey:@"centerx"]floatValue]];
pCell.centerValueLabel.text = [NSString
stringWithFormat:@"%f",[[myDictionary objectForKey:@"centery"]floatValue]];
pCell.rightValueLabel.text = [NSStringstringWithFormat:@"%f",[[myDictionary
objectForKey:@"centerz"]floatValue]];
return pCell;
break;
}
case 3: {
HorizontalPositionTableViewCell *pCell =
(HorizontalPositionTableViewCell *)[tableView
dequeueReusableCellWithIdentifier:@"HorizontalPositionTableViewCell"];
if (pCell == nil) {
pCell = [[[HorizontalPositionTableViewCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:@"HorizontalPositionTableViewCell"]autorelease];
}
pCell.enabledSetting = YES;
pCell.titleLabel.text = NSLocalizedString(@"Rotate",@"");
pCell.leftValueLabel.text = [NSString
stringWithFormat:@"%f",[[myDictionary objectForKey:@"rotatex"]floatValue]];
pCell.centerValueLabel.text = [NSString
stringWithFormat:@"%f",[[myDictionary objectForKey:@"rotatey"]floatValue]];
pCell.rightValueLabel.text = [NSString
stringWithFormat:@"%f",[[myDictionary objectForKey:@"rotatez"]floatValue]];
return pCell;
break;
}
case 4: {
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero
reuseIdentifier:@"Cell"] autorelease];
}
cell.font = [UIFont systemFontOfSize:20];
if ([myDictionary objectForKey:@"magnification"] == nil) {
[myDictionary setObject:[NSNumber numberWithFloat:1]
forKey:@"magnification"];
}
cell.text = [NSStringstringWithFormat:NSLocalizedString(@"Magnification:
%f",@""),[[myDictionary objectForKey:@"magnification"]floatValue]];
return cell;
break;
}
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"]
autorelease];
}
return cell;
}
テーブルのセルへの参照を2種類以上混在させる
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"]autorelease];
}
PositionTableViewCell *pCell = (PositionTableViewCell *)[tableView
dequeueReusableCellWithIdentifier:@"PositionTableViewCell"];
if (pCell == nil) {
pCell = [[[PositionTableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"PositionTableViewCell"] autorelease];
}
SwitchTableViewCell *sCell = (SwitchTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"SwitchTableViewCell"];
if (sCell == nil) {
sCell = [[[SwitchTableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"SwitchTableViewCell"] autorelease];
}
switch (indexPath.row) {
case 0: {
sCell.selectionStyle = UITableViewCellSelectionStyleNone;
sCell.myDictionary = self.myDictionary;
sCell.editedKey = @"display";
[sCell.switchCtl setOn:[[myDictionary objectForKey:@"display"] boolValue]
animated:NO];
sCell.nomalLabel.text = NSLocalizedString(@"Display",@"");
return sCell;
break;
}
case 1: {
cell.font = [UIFont systemFontOfSize:20];
cell.text = [NSString stringWithFormat:NSLocalizedString(@"Name:
%@", @""),[myDictionary objectForKey:@"name"]];
return cell;
break;
}
case 2: {
pCell.enabledSetting = YES;
pCell.titleLabel.text = NSLocalizedString(@"Center",@"");
pCell.leftTitleLabel.text = @"X";
pCell.leftValueLabel.text = [NSString stringWithFormat:@"%f",
[[myDictionary objectForKey:@"centerx"]floatValue]];
pCell.centerTitleLabel.text = @"Y";
pCell.centerValueLabel.text = [NSString stringWithFormat:@"%f",
[[myDictionary objectForKey:@"centery"] floatValue]];
pCell.rightTitleLabel.text = @"Z";
pCell.rightValueLabel.text = [NSString stringWithFormat:@"%f",
[[myDictionary objectForKey:@"centerz"] floatValue]];
return pCell;
break;
}
case 3: {
pCell.enabledSetting = YES;
pCell.titleLabel.text = NSLocalizedString(@"Rotate",@"");
pCell.leftTitleLabel.text = @"X";
pCell.leftValueLabel.text = [NSString stringWithFormat:@"%f",
[[myDictionary objectForKey:@"rotatex"] floatValue]];
pCell.centerTitleLabel.text = @"Y";
pCell.centerValueLabel.text = [NSString stringWithFormat:@"%f",
[[myDictionary objectForKey:@"rotatey"] floatValue]];
pCell.rightTitleLabel.text = @"Z";
pCell.rightValueLabel.text = [NSString stringWithFormat:@"%f",
[[myDictionary objectForKey:@"rotatez"] floatValue]];
return pCell;
break;
}
case 4: {
cell.font = [UIFont systemFontOfSize:20];
if ([myDictionary objectForKey:@"magnification"] == nil) {
[myDictionary setObject:[NSNumber numberWithFloat:1]
forKey:@"magnification"];
}
cell.text = [NSString stringWithFormat: NSLocalizedString (
@"Magnification: %f",@""),[[myDictionary objectForKey:
@"magnification"]floatValue]];
return cell;
break;
}
}
return cell;
}
編集モード中もセルの選択を可能にする方法
それはInterfaceBuilderのAttributes InspecterのTable Viewの項目にある、Allow Selection While Editingにチェックを入れることでした。灯台もと暗しとはまさにこの事ですね。
これで編集モード中にセルをタップするとdidSelectRowAtIndexPathがメッセージを受信するようになりますので、self.editingのBOOL値で分岐させれば通常モードと別の動きをさせることができます。この発見のおかげで、Tiny3Dのバージョン1.2.0以降では、いちいち編集画面を開かなくてもオブジェクトの表示・非表示の切り替えができるようになりました。(まだ他にも手を加えているのでアップまでにはしばらく時間がかかります。ユーザーの皆様、気長にお待ちください。)他にも気づいていない設定がいろいろありそうです(汗)。
一つ手前の階層のビューへ戻る
- AnotherViewController.mでnumberOfRowsInSectionのreturnを0から1にする。
- 前回と同様、cellForRowAtIndexPathで「cell.text = @"back";」をreturnの上の行に追加する。
- didSelectRowAtIndexPathに「[self.navigationController popViewControllerAnimated:YES];」を追加する。
ビルドして進行すると、前回と同様にテーブルが表示され、項目のタップで下の階層に移動します。ここでテーブルの項目であるbackをタップすると、上の階層のテーブルに戻ります。実際のプログラムでの応用ではdidSelectRowAtIndexPath内で戻る前に選択内容を変数に保存したり選択に伴う処理を入れれば良いわけです。なお、これらのアクションをIBActionメソッドとしておきボタンのタップ等で実行するようにすれば、テーブルではないビューでも同様の動作を行わせることができます。
お手軽ナビゲーション方法
- Navigation-Based Applicationのプロジェクトを新規作成します。プロジェクト名は何でも構いません。
- RootViewController.mで、viewWillAppearのコメントアウトを外し、中に「self.title = @"abc";」を追加します。中括弧の中であれば行の順番は気にしなくて大丈夫です。
- numberOfRowsInSectionのreturnを0から1に変更します。
- その下のcellForRowAtIndexPathの「return cell;」とある行の上にある空白の行に、「cell.text = @"123";」と記述します。
- 左側のリストのClassにTableViewControllerを新規ファイルで追加します。名前はAnotherViewControllerとします。
- RootViewController.mで冒頭の「@implementation」の上の行に「#import "AnotherViewController.h"」を追加します。
- didSelectRowAtIndexPath内のコメントのうち下3行の「//」を消します。
- 左側のリストのResourcesにView XIBを新規ファイルで追加します。名前はAnotherViewとします。
- AnotherView.xibを開き、File's OwnerのクラスをIdentity InspectorでAnotherViewControllerに変更します。
- ライブラリからTableViewをドラッグ&ドロップで追加し、もともとあるViewは消します。追加したTableViewのdataSourceとdelegateをFile's Ownerに接続します。File's OwnerのviewをTable Viewに接続します。
ビルドして進行すると、タイトルがabcという名前のテーブルが表示されます。123という項目をタップすると、右にスクロールして別のテーブルが表示されます。abcと書いてあるボタンを押すと、左にスクロールして元のテーブルに戻ります。
各セルにアクセサリーを設定する
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
return (self.editing) ? UITableViewCellAccessoryDetailDisclosureButton :
UITableViewCellAccessoryDisclosureIndicator;
}
UITableViewCellAccessoryNone:何も表示しません。
UITableViewCellAccessoryDisclosureIndicator:灰色の>を表示します。
UITableViewCellAccessoryDetailDisclosureButton:青丸に>記号が入ったボタンが表示されます。
UITableViewCellAccessoryCheckmark:チェックマークを表示します。
なお、以前の記事「前回起動時の選択項目にチェックマークを付ける」に書いたように、セルのプロパティ(accessoryType)を直接設定する方法もあります。この命令は初期設定用として、表示してからの動的な変更にaccessoryTypeを使うようにすると良いでしょう。
2009年11月7日追記
iPhone OS 3.0以降では上記のtableView:accessoryTypeForRowWithIndexPath:は使用できません。今のところは一応動きますが、ビルド時に警告が出るようになります。この場合はtableView:cellForRowAtIndexPath:内でUITableViewCellのプロパティaccessoryTypeとeditingAccessoryTypeを用いて通常及び編集時のアクセサリーを設定すると良いでしょう。