300

質問は簡単ですUITableViewCell。Xibファイルからカスタムをどのようにロードしますか?そうすることで、InterfaceBuilderを使用してセルを設計できます。メモリ管理の問題があるため、答えは明らかに単純ではありません。このスレッドは問題に言及し、解決策を提案していますが、NDAリリース前であり、コードが不足しています。これは、決定的な答えを提供せずに問題を議論する長いスレッドです。

これが私が使用したいくつかのコードです:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

このコードを使用するには、MyCell.m / .hを作成します。これは、の新しいサブクラスでUITableViewCellあり、必要なコンポーネントを追加IBOutletsします。次に、新しい「EmptyXIB」ファイルを作成します。IBでXibファイルを開き、UITableViewCellオブジェクトを追加し、その識別子を「MyCellIdentifier」に設定し、そのクラスをMyCellに設定して、コンポーネントを追加します。最後に、IBOutletsをコンポーネントに接続します。IBでファイルの所有者を設定しなかったことに注意してください。

他の方法では、ファイルの所有者を設定し、Xibが追加のファクトリクラスを介してロードされていない場合にメモリリークを警告することを推奨しています。上記をInstruments/Leaksでテストしましたが、メモリリークは見られませんでした。

では、Xibsからセルをロードするための標準的な方法は何ですか?ファイルの所有者を設定しますか?工場が必要ですか?もしそうなら、ファクトリのコードはどのように見えますか?複数の解決策がある場合は、それぞれの長所と短所を明確にしましょう...

4

23 に答える 23

307

正しい解決策は次のとおりです。

- (void)viewDidLoad
{
    [super viewDidLoad];
    UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of ItemCell
    PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    return cell;
}
于 2012-11-29T15:57:55.947 に答える
293

元の著者が IB エンジニアによって推奨されたと述べている 2 つの方法を次に示します。

詳細については、実際の投稿を参照してください。方法 2 の方が簡単に思えるので、私は方法 2 を好みます。

方法 #1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

方法 #2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

更新 (2014): 方法 #2 は引き続き有効ですが、ドキュメントはもうありません。以前は公式ドキュメントにありましたが、現在はストーリーボードのために削除されています。

Github に実例を投稿しました:
https://github.com/bentford/NibTableCellExample

Swift 4.2の編集

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}
于 2009-12-21T10:19:35.617 に答える
33

Shawn Craver の回答を参考にして、少し整理しました。

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

+ (BBCell *)cellFromNibNamed:(NSString *)nibName;

@end

BBCell.m:

#import "BBCell.h"

@implementation BBCell

+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    BBCell *customCell = nil;
    NSObject* nibItem = nil;
    while ((nibItem = [nibEnumerator nextObject]) != nil) {
        if ([nibItem isKindOfClass:[BBCell class]]) {
            customCell = (BBCell *)nibItem;
            break; // we have a winner
        }
    }
    return customCell;
}

@end

UITableViewCell の BBCell のサブクラスをすべて作成し、標準を置き換えます。

cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];

と:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
于 2010-06-22T02:15:10.500 に答える
16

ベントフォードの方法 #2を使用しました。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

動作しますが、カスタム UITableViewCell .xib ファイルでファイルの所有者への接続に注意してください。

ステートメントを渡すことowner:selfで、を のファイルの所有者として設定します。loadNibNamedUITableViewControllerUITableViewCell

IB のヘッダー ファイルにドラッグ アンド ドロップしてアクションとアウトレットを設定すると、デフォルトでファイルの所有者として設定されます。

では、所有者であるためloadNibNamed:owner:options、Apple のコードは にプロパティを設定しようとします。UITableViewControllerしかし、それらのプロパティが定義されていないため、キー値のコーディングに準拠しているというエラーが発生します。

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'

代わりにイベントがトリガーされると、NSInvalidArgumentException が発生します。

-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language:  auto; currently objective-c

UITableViewCell簡単な回避策は、ファイルの所有者ではなく、Interface Builder 接続をポイントすることです。

  1. ファイルの所有者を右クリックして、接続のリストを表示します
  2. Command-Shift-4 でスクリーン キャプチャを取得します (キャプチャする領域をドラッグして選択します)。
  3. ファイルの所有者からの接続を x アウトする
  4. オブジェクト階層で UITableCell を右クリックし、接続を再度追加します。
于 2012-03-19T21:41:43.147 に答える
14

これらの回答はどれも気に入らないので、投稿することにしました。物事は常により単純になる可能性があり、これは私が見つけた最も簡潔な方法です。

1. Interface Builder で Xib を好きなようにビルドします

  • ファイルの所有者をクラス NSObject に設定します
  • UITableViewCell を追加し、そのクラスを MyTableViewCellSubclass に設定します。IB がクラッシュした場合 (この記事の執筆時点で Xcode > 4 で発生)、UIView を使用して、Xcode 4 でインターフェイスを実行します。
  • このセル内にサブビューをレイアウトし、IBOutlet 接続を .h または .m の @interface に接続します (.m が私の好みです)

2. UIViewController または UITableViewController サブクラスで

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. MyTableViewCellSubclass で

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}
于 2013-08-26T08:18:23.720 に答える
9

Interface Builder を使用してセルを作成している場合は、インスペクターで識別子が設定されていることを確認してください。次に、dequeueReusableCellWithIdentifier を呼び出すときに同じであることを確認します。

テーブルの多いプロジェクトでいくつかの識別子を誤って設定するのを忘れてしまい、パフォーマンスの変化は昼と夜のようでした。

于 2010-04-28T10:55:12.313 に答える
8

XIB から UITableViewCells をロードすると、多くのコードが節約されますが、通常、スクロール速度が大幅に低下します (実際、これは XIB ではなく、UIView の過度の使用が原因です)。

これをご覧になることをお勧めします:リンク参照

于 2009-02-12T14:19:18.803 に答える
6

以下は、XIB からカスタム セルを作成するために使用してきたクラス メソッドです。

+ (CustomCell*) createNewCustomCellFromNib {

    NSArray* nibContents = [[NSBundle mainBundle]
                            loadNibNamed:@"CustomCell" owner:self options:NULL];

    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    CustomCell *customCell= nil;
    NSObject* nibItem = nil;

    while ( (nibItem = [nibEnumerator nextObject]) != nil) {

        if ( [nibItem isKindOfClass: [CustomCell class]]) {
            customCell = (CustomCell*) nibItem;

            if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                break; // we have a winner
            }
            else
                fuelEntryCell = nil;
        }
    }
    return customCell;
}

次に、XIB でクラス名を設定し、識別子を再利用します。その後、ビュー コントローラーの代わりにそのメソッドを呼び出すことができます。

[[UITableViewCell] alloc] initWithFrame:]

これは十分に高速で、2 つの配送アプリケーションで使用されています。を呼び出すよりも信頼性が高く[nib objectAtIndex:0]、少なくとも私の考えでは、Stephan Burlot の例よりも信頼性が高くなります。これは、適切なタイプの XIB からのみビューを取得することが保証されているためです。

于 2009-02-12T13:47:41.843 に答える
5

正解はこれ

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell  *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
    return cell; 
    }
于 2016-09-07T08:33:20.750 に答える
4

これを確認してください - http://eppz.eu/blog/custom-uitableview-cell/ - コントローラーの実装で1行で終わる小さなクラスを使用する本当に便利な方法:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}

ここに画像の説明を入力

于 2013-07-09T09:18:23.800 に答える
4

NIB のリロードにはコストがかかります。一度ロードしてから、セルが必要なときにオブジェクトをインスタンス化することをお勧めします。このメソッドを使用して、UIImageViews などを nib に、複数のセルであっても追加できることに注意してください。

したがって、私のコードは以下のとおりです-NIBを1回読み込みます(私が行ったように初期化するか、viewDidloadで何でも構いません。それ以降は、nibをオブジェクトにインスタンス化してから、必要なものを選択します。これは、nibをロードするよりもはるかに効率的です何度も。

static UINib *cellNib;

+ (void)initialize
{
    if(self == [ImageManager class]) {
        cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
        assert(cellNib);
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"TheCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(cell == nil) {
        NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
        NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                            {
                                UITableViewCell *cell = (UITableViewCell *)obj;
                                return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                            } ];
        assert(idx != NSNotFound);
        cell = [topLevelItems objectAtIndex:idx];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];

    return cell;
}
于 2011-12-14T19:01:17.143 に答える
3

これを行う正しい方法は、UITableViewCell サブクラスの実装、ヘッダー、および XIB を作成することです。XIB でビューを削除し、テーブル セルを追加するだけです。クラスを UITableViewCell サブクラスの名前として設定します。ファイルの所有者は、UITableViewController サブクラスのクラス名にします。tableViewCell アウトレットを使用して、ファイル所有者をセルに接続します。

ヘッダー ファイル内:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

実装ファイル内:

@synthesize tableViewCell = _tableViewCell;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *kCellIdentifier = @"reusableCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}
于 2012-06-28T21:57:26.280 に答える
3

What I do for this is declare an IBOutlet UITableViewCell *cell in your controller class. Then invoke the NSBundle loadNibNamed class method, which will feed the UITableViewCell to the cell declared above.

For the xib I will create an empty xib and add the UITableViewCell object in IB where it can be setup as needed. This view is then connected to the cell IBOutlet in the controller class.

- (UITableViewCell *)tableView:(UITableView *)table
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@ loading RTEditableCell.xib", [self description] );

    static NSString *MyIdentifier = @"editableCellIdentifier";
    cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];

    if(cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                      owner:self
                                    options:nil];
    }

    return cell;
}

NSBundle additions loadNibNamed (ADC login)

cocoawithlove.com article I sourced the concept from (get the phone numbers sample app)

于 2009-02-12T18:01:41.827 に答える
1

これが私の方法です: Loading Custom UITableViewCells from XIB Files… Yet Another Method

アイデアは、コードから構成する必要がある各カスタム サブビューのプロパティとプロパティをUITableViewCell使用して、 の SampleCell サブクラスを作成することです。IBOutlet UIView *content次に、SampleCell.xib ファイルを作成します。この nib ファイルで、ファイルの所有者を SampleCell に変更します。UIViewニーズに合わせたサイズのコンテンツを追加します。必要なすべてのサブビュー (ラベル、画像ビュー、ボタンなど) を追加して構成します。最後に、コンテンツ ビューとサブビューをファイル所有者にリンクします。

于 2011-03-24T11:51:19.390 に答える
0

正規の方法があるかどうかはわかりませんが、私の方法は次のとおりです。

  • ViewControllerのxibを作成します
  • ファイル所有者クラスをUIViewControllerに設定します
  • ビューを削除し、UITableViewCellを追加します
  • UITableViewCellのクラスをカスタムクラスに設定します
  • UITableViewCellの識別子を設定します
  • ビューコントローラビューのアウトレットをUITableViewCellに設定します

そして、このコードを使用します:

MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
  UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
  cell = (MyCustomViewCell *)c.view;
  [c release];
}

あなたの例では、

[nib objectAtIndex:0]

Appleがxib内のアイテムの順序を変更すると、破損する可能性があります。

于 2009-02-12T09:03:16.520 に答える
0

この拡張機能には Xcode7 beta6 が必要です

extension NSBundle {
    enum LoadViewError: ErrorType {
        case ExpectedXibToExistButGotNil
        case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        case XibReturnedWrongType
    }

    func loadView<T>(name: String) throws -> T {
        let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
        if topLevelObjects == nil {
            throw LoadViewError.ExpectedXibToExistButGotNil
        }
        if topLevelObjects.count != 1 {
            throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        }
        let firstObject: AnyObject! = topLevelObjects.first
        guard let result = firstObject as? T else {
            throw LoadViewError.XibReturnedWrongType
        }
        return result
    }
}

カスタム UITableViewCell を 1 つだけ含む Xib ファイルを作成します。

ロードします。

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
于 2015-08-28T19:44:58.393 に答える
0
 NSString *CellIdentifier = [NSString stringWithFormat:@"cell %ld %ld",(long)indexPath.row,(long)indexPath.section];


    NewsFeedCell *cell = (NewsFeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell=nil;

    if (cell == nil)
    {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:nil options:nil];

        for(id currentObject in topLevelObjects)
        {
            if([currentObject isKindOfClass:[NewsFeedCell class]])
            {
                cell = (NewsFeedCell *)currentObject;
                break;
            }
        }
}
return cell;
于 2014-06-11T06:38:13.617 に答える