UITableView reloadDataでnumberOfSectionsInTableViewが呼ばれない

ユーザーが別画面で情報編集後、一覧画面のテーブルビューを更新したいことがあります。

テーブルビュー側の viewWillAppear で reloadData を実行すればテーブルが更新されますが、
[super viewWillAppear:animated]との順番によって動作が変わる点に注意しなくてはなりません。

<NGの例>

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.tableView reloadData];
}

一見よさそうですが、以下の2点で問題があります。
・ビューの最初の読み込み時に、numberOfSectionsInTableView が重複して呼ばれてしまう
・他のビューから戻ってきた時に、numberOfSectionsInTableView や numberOfRowsInSection が呼ばれず、cellForRowAtIndexPath だけ呼ばれてしまう可能性がある。セクション数などが変わった場合、Appが落ちる原因となる。

<OKの例>

- (void)viewWillAppear:(BOOL)animated {
    [self.tableView reloadData]; // OK
    [super viewWillAppear:animated];
}

上記2点の問題が解消されます。

UIPickerViewのselectRowが動かないとき

例えば、ピッカーを2つ設置したとします。

一方のピッカーの選択によって、他方のピッカーの値をselectRow:inComponentによって変えるとき、
その他方のピッカーが動かないことがありました。

以下のようなコードです。

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    /* ... */

    // 選択させる
    [pickerView selectRow:rowIndex inComponent:0 animated:YES];
    // 選択後の動作をさせる
    [self pickerView:pickerView didSelectRow:rowIndex inComponent:0];
}

ネットで情報がうまく得られなかったため、以下のようにperformSelectorを使ったところ、解決しました。

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    /* ... */
    _index = rowIndex;
    [self performSelector:@selector(selectPicker2:) withObject:self afterDelay:0.01];
}
- (void)selectPicker2:(id)sender
{
    [_picker2 selectRow:_index inComponent:0 animated:YES];
    [self pickerView:_picker2 didSelectRow:_index inComponent:0];
}

iOSシミュレータで音が鳴らない/ファイルが読み込めないことがある

いつも通りビルドしたつもりなのに、突然下記の症状が出ることがあります。

  • 音声が再生されない
  • 画像が表示されない
  • ファイルが読み込めない

特に音については、CocosDenshion(SimpleAudioEngine)の使用方法や
caf/mp3/wav/aifファイルの作成方法、
afconvertコマンドでの引数指定を間違えたのか、と思いがちです。

トリガーとなるのは、直前のクリーンとリソースファイルの登録し直しです。

原因は、
Project Navigator のプロジェクト→TARGETS→Build Phases→Copy Bundle Resources
に該当のリソースファイルが登録されていないからです。
しかしここに追加しても根本的な解決になりません。

.png以外のリソースファイルをXcodeウィンドウにドラッグアンドドロップすると、
直後の「Choose options for adding these files」ダイアログにて
Add to targetsオプションでターゲットのチェックが外れています。
(.pngファイルだと、ターゲットにデフォルトでチェックが付きます)

よって、リソースファイルが更新されてもコピーされずに以前のデータが使われていたが、
クリーンしたことでそのデータも消えてしまった、というわけです。

ドラッグアンドドロップ後のダイアログによく注意し、
ターゲットにチェックを付けるようにしましょう。

UITableViewをコードで追加しスクロールバーを点滅させる

InterfaceBuilderを使わずにUITableViewを作成したとき、

UITableView* tableView = [[UITableView alloc] init];
[_controller.view addSubview:tableView];
[tableView flashScrollIndicators];
/* or */
[tableView performSelector:@selector(flashScrollIndicators)];

としてもスクロールバーが点滅しないことがあります。
以下のように、performSelector:withObject:afterDelay: を使うと点滅させることができます。

UITableView* tableView = [[UITableView alloc] init];
...
[tableView performSelector:@selector(flashScrollIndicators) withObject:self afterDelay:0.0];

Xcodeをアップデートするとビルドエラーが発生する

Xcodeをアップデートした後の最初のビルド時に、
以下のようなエラーが発生することがあります。


error: PCH file built from a different branch ((clang-425.0.27)) than the compiler ((clang-425.0.28))

この時は、一旦クリーンしてビルドし直すとエラーが解消されます。
Product → Clean

なお、シミュレータ、実機のそれぞれでクリーンが必要になります。

アプリの画面を横向きに固定する

Xcodeのプロジェクト設定(TARGETS設定)の
iPhone / iPod Deployment Info → Supported Interface Orientations
にて
「Landscape Left」 or 「Landscape Right」
を選択しても横画面にならないときは、
ViewControllerのサブクラスにて下記のメソッドを追加します。

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    BOOL ret = NO;
    if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) ret = YES;
    return ret;
}

このtoInterfaceOrientation には、向きが入ってきますので、許可する向きの値のときに
YESを返すようにします。

UITableViewのセクションのタイトル行を非表示にする

セクションのタイトル行(バー)そのものを非表示にするには、tableView:titleForHeaderInSection: で空文字列(@””)を返します。

- (NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return @"";
}

セクションが複数あったとしても、非表示になります。

UITableViewの編集モードで移動マークを表示させる

UITableViewDelegateを実装したUIViewControllerのサブクラスで、
以下のメソッドを定義すると、移動マークが表示されてドラッグにより移動可能になります。

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{    
    // 移動後のデータ並び替えのコードを記述
}

UITableViewで横スワイプ時に削除ボタンを表示させる

UITableViewDelegateを実装するUIViewControllerのサブクラスにて
tableView:commitEditingStyle:forRowAtIndexPath:
のメソッドを定義すると、
横スワイプで削除ボタンが表示されるようになります。

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        // 削除するコードを挿入します
    }
}

actionは都度新規生成する必要がある

CCShow, CCHideなどの CCAction系クラスのインスタンスは、再利用すると動作がおかしくなります。
[CCShow action]などと、利用する都度新規生成しなくてはなりません。
(これに気付かず数時間を費やしてしまいました)

参考

以下がそのNGコードです。

id acShow = [CCShow action];
id acDelay = [CCDelayTime actionWithDuration:1.0];
id acHide = [CCHide action];
[sprite1 runAction:[CCSequence actions:adShow,acDelay,acHide,nil]];
[sprite2 runAction:[CCSequence actions:adShow,acDelay,acHide,nil]];

正しくは、actions内で都度生成する必要があります。