web-dev-qa-db-ja.com

個別のデリゲート/データソースを使用する場合のUITableViewの問題

概要:

動作するものから始めるために、Interface Builderを使用してXcodeで生成されたビューに配置されたUITableViewがあります。ビューのファイル所有者は、Xcodeが生成したUIViewControllerのサブクラスに設定されます。このサブクラスにnumberOfSectionsInTableView: tableView:numberOfRowsInSection:tableView:cellForRowAtIndexPath:の実装を追加しました。TableViewのdataSourcedelegateは、Interface BuilderのFile Ownerを介してこのクラスに接続されています。

上記の構成は問題なく機能します。このテーブルビューのdataSourceおよびdelegate- implementationsを別のクラスに移動したい場合に問題が発生します。これは、おそらくテーブルビュー以外にビューに他のコントロールがあり、テーブルビュー関連のコードを移動したいためです。独自のクラスに。これを達成するために、私は次を試みます:

  • XcodeでUITableViewControllerの新しいサブクラスを作成します
  • numberOfSectionsInTableView:tableView:numberOfRowsInSection:、およびtableView:cellForRowAtIndexPath:の既知の実装を新しいサブクラスに移動します
  • UITableViewControllerをInterfaceBuilderのexistingXIBの最上位にドラッグし、このUIViewに対して自動的に作成されるUITableView/UITableViewControllerを削除し、UITableViewControllerのクラスを新しいサブクラスに一致するように設定します
  • 以前に機能していたUITableViewの既存のdataSourceおよびdelegate接続を削除し、それらを新しいUITableViewControllerに接続します

完了すると、UITableViewが機能しません。ランダムに発生する可能性のある3つの結果のいずれかになります。

  • UITableViewがロードされると、tableView:cellForRowAtIndexPath:を認識していないオブジェクトに送信していることを示すランタイムエラーが表示されます。
  • UITableViewがロードされると、プロジェクトはエラーなしでデバッガーにブレークインします
  • エラーはありませんが、UITableViewは表示されません

デバッグを行い、この問題を再現するために基本的なプロジェクトを作成した場合、通常、上記の3番目のオプションが表示されます(エラーは表示されませんが、テーブルビューは表示されません)。 NSLog呼び出しをいくつか追加したところ、numberOfSectionsInTableView:numberOfRowsInSection:の両方が呼び出されていますが、cellForRowAtIndexPath:は呼び出されていません。私は本当にシンプルなものを見逃していると確信しており、私よりも多くの経験を持つ人には答えが明らかであることを望んでいた。これが簡単な答えにならない場合は、いくつかのコードまたはサンプルプロジェクトで更新したいと思います。御時間ありがとうございます!

完全な再現手順:

  • Xcodeで新しいiPhone OS、ビューベースアプリケーションを作成し、TableTestと呼びます
  • Interface BuilderでTableTestViewController.xibを開き、UITableViewを提供されたビューサーフェスにドラッグします。
  • UITableViewdataSourceおよびdelegate- outletsをFile's Ownerに接続します。これは、既にTableTestViewController- classを表しているはずです。変更を保存します
  • Xcodeに戻り、次のコードをTableTestViewController.m:に追加します

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"Returning num sections");
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSLog(@"Returning num rows");
    return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Trying to return cell");
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }
    cell.text = @"Hello";
    NSLog(@"Returning cell");
    return cell;
}
  • ビルドして実行すると、HelloにWord UITableViewが表示されるはずです。

  • このUITableViewのロジックを別のクラスに移動するには、まずXcodeで新しいファイルを作成し、UITableViewControllerサブクラスを選択して、クラスTableTestTableViewControllerを呼び出します

  • 上記のコードスニペットをTableTestViewController.mから削除してTableTestTableViewController.mに配置し、これら3つのメソッドのデフォルトの実装を当社のものに置き換えます。
  • 同じTableTestViewController.xib-ファイル内のInterface Builderに戻り、UITableViewControllerをメインIBウィンドウにドラッグし、自動的に付属する新しいUITableViewオブジェクトを削除します
  • この新しいUITableViewControllerのクラスをTableTestTableViewControllerに設定します
  • 以前使用していた既存のdataSourceからdelegateおよびUITableViewバインディングを削除し、作成した新しいTableTestTableViewControllerに同じ2つのバインディングを再接続します。
  • 変更を保存し、ビルドして実行します。結果が表示されている場合は、UITableViewが適切に機能しなくなっていることに注意してください。

解決策:さらにトラブルシューティングと iPhone開発者フォーラム からの支援を受けて、解決策を文書化しました!プロジェクトのメインUIViewControllerサブクラスには、UITableViewControllerインスタンスを指すアウトレットが必要です。これを実現するには、次をプライマリビューのヘッダー(TableTestViewController.h)に追加するだけです。

#import "TableTestTableViewController.h"

そして

IBOutlet TableTestTableViewController *myTableViewController;

次に、Interface Builderで、File's Ownerの新しいアウトレットをメインIBウィンドウのTableTestTableViewControllerに接続します。 XIBのUI部分を変更する必要はありません。このアウトレットを配置するだけで、ユーザーコードが直接使用しない場合でも、問題は完全に解決します。支援してくれた人たちに感謝し、解決策を見つけるためにiPhone開発者フォーラムのBaldEagleにクレジットを提供します。

54
Adam Alexander

私はあなたの手順に従い、プロジェクトを再作成し、同じ問題に遭遇しました。基本的にあなたはほとんどそこにいます。欠けているものが2つあります(修正後は動作します)。

  • tableViewTableTestTableViewControllerを画面にあるUITableViewに接続する必要があります。 IBOutletではないので前に言ったように、tableViewプロパティをオーバーライドして、IBOutletにすることができます:

    @interface TableTestTableViewController : UITableViewController {
        UITableView *tableView;
    }
    
    @property (nonatomic, retain) IBOutlet UITableView *tableView;
    
  • 次に、TableTestTableViewControllerへの参照を追加し、TableTestViewControllerに保持します。そうしないと、TableTestTableViewControllerが解放される可能性があり(ペン先に何もぶら下がっていない状態でロードした後)、そのために不安定な結果、クラッシュ、または何も表示されません。追加するには:

    @interface TableTestViewController : UIViewController {
        TableTestTableViewController *tableViewController;
    }
    
    @property (nonatomic, retain) IBOutlet TableTestTableViewController  *tableViewController;
    

    それをInterface BuilderでTableTestTableViewControllerインスタンスに接続します。

上記で、これは私のマシンでうまくいきました。

また、(すべてのUITableViewControllerUITableViewを使用する代わりに)このすべての背後にある動機を述べることは良いと思います。私の場合、同じ画面いっぱいのコンテンツでUITableViewだけを使用する他のビューを使用することでした。したがって、UILabelsの下に他のUIImagesまたはUIViewを追加し、それらの下または上にUITableViewを表示できます。

23
keremk

何らかの理由ではい(誰かが理由を知っているならチャイムしてください...)tableViewUITableViewControllerプロパティは、パブリックプロパティであってもIBOutletとして公開されません。したがって、Interface Builderを使用する場合、他のUITableViewに接続するためのプロパティは表示されません。そのため、サブクラスで、tableViewとしてマークされたIBOutletプロパティを作成し、それを接続できます。

これはすべてハックであり、回避策のようですが、UITableViewController'sUITableViewを分離してUI階層の別の場所に配置する唯一の方法のようです。 UITableView以外のものがあるビューをデザインしようとしたときに同じ問題に遭遇しました。それが解決方法でした...これは正しいアプローチですか?

3
keremk

この作品を作ることができました。 4つのTableViewとビデオ付きのWebビューを備えた、本当にすてきなダッシュボードを作成しました。重要なのは、個別のtableViewコントローラーと、View Controllerで定義されている他のTableViewコントローラーへのIBOutletsを持つことです。 UIBでは、他のTableView ControllerをView Controllerのファイル所有者に接続するだけです。次に、テーブルをデータソースとデリゲートの対応するView Controllerに接続します。

2
user589642