作業中のアプリでストーリーボードを使用しようとしています。アプリにはListsとsersがあり、それぞれに他のコレクション(リストのメンバー、ユーザーが所有するリスト)が含まれています。したがって、それに応じて、ListCell
およびUserCell
クラスがあります。目標は、それらをアプリ全体で(つまり、私のtableviewコントローラーで)再利用できるようにすることです。
それは私が問題に直面しているところです。
どのようにView Controllerで再利用できるカスタムTableviewセルをストーリーボードに作成しますか?
ここに、私がこれまでに試した具体的なものを示します。
コントローラ#1で、プロトタイプセルを追加し、クラスをUITableViewCell
サブクラスに設定し、再利用IDを設定し、ラベルを追加して、クラスのアウトレットに配線しました。コントローラ#2で、空のプロトタイプセルを追加し、同じクラスに設定して、以前と同じidを再利用します。実行すると、コントローラー#2にセルが表示されるときにラベルが表示されることはありません。コントローラ#1で正常に動作します。
異なるNIBで各セルタイプを設計し、適切なセルクラスに配線しました。ストーリーボードで、空のプロトタイプセルを追加し、そのクラスを設定し、IDを再利用してセルクラスを参照します。コントローラーのviewDidLoad
メソッドで、それらのNIBファイルを再利用IDに登録しました。表示されると、両方のコントローラーのセルはプロトタイプのように空でした。
両方のコントローラーのプロトタイプを空にしてクラスを設定し、IDをセルクラスに再利用しました。セルのUIを完全にコードで構築しました。セルはすべてのコントローラーで完全に機能します。
2番目のケースでは、プロトタイプが常にNIBをオーバーライドしていると思われます。プロトタイプセルを削除した場合、再利用IDにNIBを登録すると機能します。しかし、その後、セルから他のフレームにセグエを設定することはできません。これは、ストーリーボードを使用する実際のポイントです。
結局のところ、2つのことが必要です。ストーリーボードでテーブルビューベースのフローを結び付け、コードではなく視覚的にセルレイアウトを定義します。今のところ、これらの両方を取得する方法がわかりません。
私はそれを理解しているように、あなたがしたい:
残念ながら、現在これを行う方法はありません。以前の試行が機能しなかった理由を理解するには、ストーリーボードとプロトタイプテーブルビューセルの機能についてさらに理解する必要があります。 (whyを気にしない場合は、これらの他の試みが機能しなかったので、今すぐお任せください。魔法の回避策はありません。 、バグを報告することを提案する以外。)
ストーリーボードは、本質的に、.xibファイルのコレクションにすぎません。ストーリーボードからいくつかのプロトタイプセルがあるTable View Controllerをロードすると、次のようになります。
-[UITableView registerNib:forCellReuseIdentifier:]
が呼び出されます。-[UITableView dequeueReusableCellWithIdentifier:]
を呼び出します特定の再利用識別子を持つセルを要求すると、nibが登録されているかどうかがチェックされます。存在する場合、そのセルのインスタンスをインスタンス化します。これは、次の手順で構成されます。
[[CellClass alloc] initWithCoder:]
を呼び出します。-initWithCoder:
メソッドは、サブビューを追加して、nibで定義されたプロパティを設定します。 (IBOutlet
sもおそらくここに接続されますが、テストしていませんが、-awakeFromNib
で発生する可能性があります)必要に応じてセルを構成します。
ここで注意すべき重要な点は、セルのclassと視覚的外観セルの。同じクラスの2つの別々のプロトタイプセルを作成できますが、サブビューのレイアウトはまったく異なります。実際、デフォルトのUITableViewCell
スタイルを使用する場合、これがまさに起こっていることです。たとえば、「デフォルト」スタイルと「字幕」スタイルは、両方とも同じUITableViewCell
クラスで表されます。
これは重要です:セルのクラスは、特定のビュー階層と1対1の相関関係はありません。ビュー階層は、この特定のコントローラーに登録されたプロトタイプセルの内容によって完全に決定されます。
また、セルの再利用識別子がグローバルセルディスペンサリーに登録されていなかったことにも注意してください。再利用識別子は、単一のUITableView
インスタンスのコンテキスト内でのみ使用されます。
この情報が与えられたら、上記の試みで何が起こったのか見てみましょう。
コントローラ#1で、プロトタイプセルを追加し、クラスをUITableViewCellサブクラスに設定し、再利用IDを設定し、ラベルを追加して、それらをクラスのアウトレットに配線しました。コントローラ#2で、空のプロトタイプセルを追加し、同じクラスに設定して、以前と同じidを再利用します。実行すると、コントローラー#2にセルが表示されるときにラベルが表示されることはありません。コントローラ#1で正常に動作します。
これは予想されることです。両方のセルに同じクラスがありましたが、コントローラー#2のセルに渡されたビュー階層にはサブビューがまったくありませんでした。これで空のセルができました。これはまさにプロトタイプに入れたものです。
異なるNIBで各セルタイプを設計し、適切なセルクラスに配線しました。ストーリーボードで、空のプロトタイプセルを追加し、そのクラスを設定し、IDを再利用してセルクラスを参照します。コントローラーのviewDidLoadメソッドで、これらのNIBファイルを再利用IDに登録しました。表示されると、両方のコントローラーのセルはプロトタイプのように空でした。
繰り返しますが、これは期待されています。再利用識別子は、ストーリーボードのシーンやニブ間で共有されないため、これらの異なるセルのすべてが同じ再利用識別子を持っているという事実は無意味でした。テーブルビューから取得したセルは、ストーリーボードのそのシーンのプロトタイプセルに一致する外観になります。
しかし、この解決策は近かった。前述のように、プログラムで-[UITableView registerNib:forCellReuseIdentifier:]
を呼び出して、セルを含むUINib
を渡すだけで、同じセルを取得できます。 (これは、プロトタイプがペン先を「オーバーライド」していたためではありません。単にテーブルビューでペン先を登録していなかったため、ストーリーボードに埋め込まれたペン先を見続けていました。)残念ながら、このアプローチには欠陥があります—ストーリーボードのセグエをスタンドアロンのペン先のセルに接続する方法はありません。
両方のコントローラーのプロトタイプを空にしてクラスを設定し、IDをセルクラスに再利用しました。セルのUIを完全にコードで構築しました。セルはすべてのコントローラーで完全に機能します。
当然。うまくいけば、これは当然のことです。
だから、それはうまくいかなかった理由です。スタンドアロンのペン先でセルを設計し、複数のストーリーボードシーンで使用できます。現在、これらのセルにストーリーボードセグエを接続することはできません。ただし、これを読んでいる過程で何かを学んだことを願っています。
BJホーマーによるすばらしい答えにもかかわらず、私には解決策があるように感じます。私のテストに関する限り、それは機能します。
概念:xibセルのカスタムクラスを作成します。そこで、タッチイベントを待って、プログラムでセグエを実行できます。ここで必要なのは、セグエを実行するコントローラーへの参照です。私の解決策は、tableView:cellForRowAtIndexPath:
で設定することです。
複数のテーブルビューで使用するテーブルセルを含むDetailedTaskCell.xib
があります。
そのセルにはカスタムクラスTaskGuessTableCell
があります:
これは魔法が起こるところです。
// TaskGuessTableCell.h
#import <Foundation/Foundation.h>
@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end
// TashGuessTableCell.m
#import "TaskGuessTableCell.h"
@implementation TaskGuessTableCell
@synthesize controller;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSIndexPath *path = [controller.tableView indexPathForCell:self];
[controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
[controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
[super touchesEnded:touches withEvent:event];
}
@end
複数のセグエがありますが、それらはすべて同じ名前です:"FinishedTask"
。ここで柔軟にする必要がある場合は、別のプロパティを追加することをお勧めします。
ViewControllerは次のようになります。
// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"
@implementation LogbookViewController
- (void)viewDidLoad
{
[super viewDidLoad]
// register custom nib
[self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TaskGuessTableCell *cell;
cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
cell.controller = self; // <-- the line that matters
// if you added the seque property to the cell class, set that one here
// cell.segue = @"TheSegueYouNeedToTrigger";
cell.taskTitle.text = [entry title];
// set other outlet values etc. ...
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:@"FinishedTask"])
{
// do what you have to do, as usual
}
}
@end
同じことを達成するためのよりエレガントな方法があるかもしれませんが、それは動作します! :)
私はこれを探していましたが、リチャード・ヴェナブルの この答え を見つけました。わたしにはできる。
iOS 5には、UITableViewの新しいメソッドregisterNib:forCellReuseIdentifierが含まれています。
それを使用するには、UITableViewCellをnibに入れます。これは、ペン先の唯一のルートオブジェクトでなければなりません。
TableViewをロードした後、nibを登録できます。セル識別子を使用してdequeueReusableCellWithIdentifier:を呼び出すと、Storyboardプロトタイプセルを使用した場合と同様に、nibからnibを取得します。
Swift
BJホーマーはすばらしい説明をしてくれました。コンセプトを理解するのに役立ちます。 TableViewControllerで使用できるmake a custom cell reusable in storyboard
には、mix the Storyboard and xib
アプローチが必要です。 CustomCell
およびTableViewControllerOne
で使用されるTableViewControllerTwo
という名前のセルがあるとします。私は段階的にそれを作っています。
1。 [ファイル]> [新規]> [ファイル]> [Cocoa Touchクラスを選択]> [次へ]>クラスの名前を指定します(たとえばCustomCell
)> UITableVieCellとしてサブクラスを選択>ファイルのチェックボックスをオンにし、[次へ]を押します。
2。必要に応じてセルをカスタマイズし、セルの属性インスペクターで識別子を設定します。ここではCellIdentifier
として設定します。この識別子は、セルを識別して再利用するためにViewControllerで使用されます。
。これで、ViewController viewDidLoad
でregister this cell
を実行するだけです。初期化メソッドは必要ありません。
4。これで、任意のtableViewでこのカスタムセルを使用できます。
TableViewControllerOneで
let reuseIdentifier = "CellIdentifier"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell
return cell!
}
BJホーマーは、何が起こっているのかについて優れた説明をしてくれました。
実用的な観点から、セルをxibとして使用できず、セグエを接続できないことを考えると、セルをxibとして選択するのが最良の選択です。 、とにかくセグエが異なるコントローラーと異なる可能性があります。テーブルビューコントローラーから次のコントローラーにセグエを直接定義し、コードで実行できます。 。
さらに注意すべき点は、セルを独立したxibファイルとして使用すると、アクションなどをTable View Controllerに直接接続できないことです(とにかくこれを解決していません-ファイルの所有者を意味のあるものとして定義することはできません)。セルのTable View Controllerが準拠することが期待されるプロトコルを定義し、cellForRowAtIndexPathのデリゲートに似た弱いプロパティとしてコントローラを追加することで、これを回避しています。
セグエのテストではなく、同じVCのセルをロードする方法を見つけました。これは、別のペン先でセルを作成するための回避策になる可能性があります
1つのVCと2つのテーブルがあり、ストーリーボードでセルを設計し、両方のテーブルで使用したいとします。
(例:結果のテーブルを持つUISearchControllerを持つテーブルと検索フィールドで、両方で同じCellを使用したい場合)
コントローラーがセルを要求したら、次のようにします。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * identifier = @"CELL_ID";
ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
// Ignore the "tableView" argument
}
そして、ここにはストーリーボードからのセルがあります