Làm cách nào để bạn tải UITableViewCells tùy chỉnh từ các tệp Xib?


293

Câu hỏi rất đơn giản: Làm thế nào để bạn tải tùy chỉnh UITableViewCelltừ các tệp Xib? Làm như vậy cho phép bạn sử dụng Trình tạo giao diện để thiết kế các ô của mình. Câu trả lời rõ ràng là không đơn giản do vấn đề quản lý bộ nhớ. Chủ đề này đề cập đến vấn đề và đề xuất một giải pháp, nhưng là phát hành trước NDA và thiếu mã. Đây là một chủ đề dài thảo luận về vấn đề mà không cung cấp một câu trả lời dứt khoát.

Đây là một số mã tôi đã sử dụng:

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];
}

Để sử dụng mã này, hãy tạo MyCell.m / .h, một lớp con mới UITableViewCellvà thêm IBOutletscho các thành phần bạn muốn. Sau đó tạo một tệp "rỗng XIB" mới. Mở tệp Xib trong IB, thêm một UITableViewCellđối tượng, đặt định danh của nó thành "MyCellIdentifier" và đặt lớp của nó thành MyCell và thêm các thành phần của bạn. Cuối cùng, kết nối IBOutletsvới các thành phần. Lưu ý rằng chúng tôi đã không đặt Chủ sở hữu tệp trong IB.

Các phương pháp khác ủng hộ việc thiết lập Chủ sở hữu tệp và cảnh báo rò rỉ bộ nhớ nếu Xib không được tải thông qua một lớp nhà máy bổ sung. Tôi đã thử nghiệm ở trên trong Dụng cụ / Rò rỉ và không thấy rò rỉ bộ nhớ.

Vì vậy, cách chính tắc để tải các tế bào từ Xibs là gì? Chúng tôi có đặt Chủ sở hữu tệp không? Chúng ta có cần một nhà máy không? Nếu vậy, mã cho nhà máy trông như thế nào? Nếu có nhiều giải pháp, hãy làm rõ ưu và nhược điểm của từng giải pháp ...


2
Ai đó có thể chỉnh sửa chủ đề để thực sự đặt câu hỏi, nghĩa là "Làm thế nào để bạn tải UITableViewCells tùy chỉnh từ các tệp Xib?" (Bỏ qua nếu điều này không thể xảy ra trên stackoverflow.)
Steven Fisher

1
Đối với iOS 5 trở lên, đây là giải pháp: stackoverflow.com/questions/15591364/ , giống như giải pháp của giuseppe.
Matt Becker

Ghi chú nhanh, đơn giản hơn (năm 2013) trả lời tại đây stackoverflow.com/questions/15378788/ Nhật jamihash
Fattie

Câu trả lời:


288

Đây là hai phương pháp mà các tác giả gốc đã đề xuất bởi một kỹ sư IB .

Xem bài viết thực tế để biết thêm chi tiết. Tôi thích phương pháp # 2 vì nó có vẻ đơn giản hơn.

Phương pháp số 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;
}

Phương pháp # 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;
}

Cập nhật (2014): Phương pháp # 2 vẫn còn hiệu lực nhưng không có tài liệu nào cho nó nữa. Nó từng là trong các tài liệu chính thức nhưng bây giờ được gỡ bỏ theo hướng có lợi cho các bảng phân cảnh.

Tôi đã đăng một ví dụ hoạt động trên Github:
https://github.com/bentford/NibTableCellExample

chỉnh sửa cho 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
}

1
Đối với phương thức 1, bạn không nên làm một cái gì đó như "cell = (BDCustomCell *) [[tạm thời Trình điều khiển.view giữ lại] autorelease];" Vì vậy, tế bào không được phát hành khi bộ điều khiển tạm thời được phát hành?
Tod Cickyham

Hừm. Tài liệu nói về # 2 vẫn bảo bạn đặt chủ sở hữu của ô trong tệp XIB, thành một lớp trình điều khiển đã biết. Có lẽ nó không thành vấn đề khi bạn đặt chủ sở hữu trong khi tải.
Oscar

@OscarGoldman Chủ sở hữu của ô trong tệp XIB là một lớp (tức là loại chủ sở hữu.) Chủ sở hữu của ô trong loadNibNamed: chủ sở hữu: tùy chọn: là một đối tượng của loại được chỉ định trong XIB.
bentford

2
@CoolDocMan Tùy chọn # 2 vẫn hoạt động. Vấn đề rất có thể với ngòi. Dưới đây là một ví dụ: github.com/bentford/NibTableCellExample
bentford

2
Tại sao mã siêu cũ này được xếp hạng cao như vậy. Stackoverflow làm một cái gì đó: /
Nico S.

304

Giải pháp đúng là đây:

- (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;
}

điều đó sẽ phá vỡ các ứng dụng iOS5? Tôi thực sự chưa bao giờ thấy UINib
Adam Waite

@AdamWaite Đăng ký tệp NIB hoạt động cho iOS 5 trở lên, vì vậy nó không phá vỡ các ứng dụng iOS 5. Và UINib thậm chí còn tồn tại kể từ iOS 4.
Mecki

Để có một ví dụ tốt, hãy kiểm tra repo git được tham chiếu trong câu trả lời hàng đầu ở đây: stackoverflow.com/questions/18746929/ chủ
netigger 17/03/2016

39

Đăng ký

Sau iOS 7, quá trình này đã được đơn giản hóa thành ( swift 3.0 ):

// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")

// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")

( Lưu ý ) Điều này cũng có thể đạt được bằng cách tạo các ô trong .xibhoặc .stroyboardtệp, dưới dạng các ô nguyên mẫu. Nếu bạn cần đính kèm một lớp cho chúng, bạn có thể chọn nguyên mẫu ô và thêm lớp tương ứng (tất nhiên phải là hậu duệ của UITableViewCell).

Dequeue

Và sau này, đã sử dụng ( swift 3.0 ):

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text = "Hello"

    return cell
}

Sự khác biệt là phương pháp mới này không chỉ làm mất tế bào mà còn tạo ra nếu không tồn tại (điều đó có nghĩa là bạn không phải thực hiện if (cell == nil)shenanigans) và tế bào đã sẵn sàng sử dụng như trong ví dụ trên.

( Cảnh báo ) tableView.dequeueReusableCell(withIdentifier:for:)có hành vi mới, nếu bạn gọi hành vi khác (không có indexPath:) bạn sẽ có hành vi cũ, trong đó bạn cần tự kiểm tra nilvà thể hiện hành vi đó, hãy chú ý UITableViewCell?giá trị trả về.

if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
    // Cell be casted properly
    cell.myCustomProperty = true
}
else
{
    // Wrong type? Wrong identifier?
}

Và tất nhiên, loại của lớp liên kết của ô là loại bạn đã xác định trong tệp .xib cho UITableViewCelllớp con, hoặc cách khác, sử dụng phương thức đăng ký khác.

Cấu hình

Lý tưởng nhất là các ô của bạn đã được cấu hình về mặt ngoại hình và định vị nội dung (như nhãn và chế độ xem hình ảnh) vào thời điểm bạn đăng ký chúng và trên cellForRowAtIndexPathphương thức bạn chỉ cần điền chúng vào.

Tất cả cùng nhau

class MyCell : UITableViewCell
{
    // Can be either created manually, or loaded from a nib with prototypes
    @IBOutlet weak var labelSomething : UILabel? = nil
}

class MasterViewController: UITableViewController 
{
    var data = ["Hello", "World", "Kinda", "Cliche", "Though"]

    // Register
    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
        // or the nib alternative
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    // Dequeue
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell

        cell.labelSomething?.text = data[indexPath.row]

        return cell
    }
}

Và tất nhiên, đây là tất cả có sẵn trong ObjC với cùng tên.


Đây là phiên bản objC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb

33

Lấy câu trả lời của Shawn Craver và dọn dẹp nó một chút.

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

Tôi tạo tất cả các lớp con của UITableViewCell của BBCell, và sau đó thay thế tiêu chuẩn

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

với:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];

16

Tôi đã sử dụng Phương pháp # 2 của bentford :

- (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;
}

Nó hoạt động, nhưng coi chừng các kết nối đến Chủ sở hữu tệp trong tệp UITableViewCell .xib tùy chỉnh của bạn.

Bằng cách chuyển owner:selfvào loadNibNamedtuyên bố của bạn , bạn đặt UITableViewControllerChủ sở hữu tệp của bạn UITableViewCell.

Nếu bạn kéo và thả vào tệp tiêu đề trong IB để thiết lập các hành động và cửa hàng, nó sẽ thiết lập chúng làm Chủ sở hữu tệp theo mặc định.

Trong loadNibNamed:owner:options, mã của Apple sẽ cố gắng đặt thuộc tính cho bạn UITableViewController, vì đó là chủ sở hữu. Nhưng bạn không có các thuộc tính được xác định ở đó, do đó bạn gặp lỗi về việc tuân thủ mã hóa giá trị chính :

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

Nếu một Sự kiện được kích hoạt thay vào đó, bạn sẽ nhận được 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

Một cách giải quyết dễ dàng là trỏ các kết nối Trình tạo giao diện của bạn vào UITableViewCellthay vì Chủ sở hữu tệp:

  1. Nhấp chuột phải vào Chủ sở hữu tệp để hiển thị danh sách các kết nối
  2. Chụp ảnh màn hình bằng Command-Shift-4 (kéo để chọn khu vực cần chụp)
  3. x ra các kết nối từ Chủ sở hữu tệp
  4. Nhấp chuột phải vào UITableCell trong hệ thống phân cấp Đối tượng và thêm lại các kết nối.

Tôi đã gặp sự cố mà bạn đã đề cập, nhưng làm cách nào để trỏ các kết nối đến UITableViewCell thay vì chủ sở hữu của Tệp? Tôi không hiểu các bước của bạn, ví dụ tại sao cần chụp ảnh màn hình? và khi tôi nhấp vào nút thêm bên cạnh ổ cắm, không có gì xảy ra
xu huanze 23/03/13

@xuhuanze Tôi đề nghị chụp ảnh màn hình để bạn có bản ghi về những thứ mà chủ sở hữu của Tệp đã được kết nối. Sau đó, bạn có thể tạo lại những kết nối tương tự. Bạn cần kéo và thả để thêm các kết nối - không chỉ bằng một cú nhấp chuột.
funroll

Cảm ơn rất nhiều, tôi đã gặp vấn đề "lớp này không phải là khóa mã hóa giá trị chính cho khóa" và giải quyết nó bằng sự giúp đỡ của bạn. Tôi muốn nói với những người khác rằng bạn cũng nên thay đổi một lớp UITableViewCell thành lớp của bạn, mà bạn sử dụng như một lớp ô tùy chỉnh.
Denis Kutlubaev

14

Tôi đã quyết định đăng bài vì tôi không thích bất kỳ câu trả lời nào trong số này - mọi thứ luôn có thể đơn giản hơn và đây là cách ngắn gọn nhất mà tôi tìm thấy.

1. Xây dựng Xib của bạn trong Trình tạo giao diện như bạn muốn

  • Đặt chủ sở hữu tệp thành lớp NSObject
  • Thêm một UITableViewCell và đặt lớp của nó thành MyTableViewCellSub class - nếu IB của bạn gặp sự cố (xảy ra trong Xcode> 4 khi viết bài này), chỉ cần sử dụng UIView của giao diện trong Xcode 4 nếu bạn vẫn đặt nó xung quanh
  • Bố trí các cuộc phỏng vấn của bạn bên trong ô này và đính kèm các kết nối IBOutlet của bạn vào @interface của bạn trong .h hoặc .m (.m là tùy chọn của tôi)

2. Trong lớp con UIViewControll hoặc UITableViewControll của bạn

@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. Trong MyTableViewCellSub class của bạn

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

    return self;
}

9

Nếu bạn đang sử dụng Trình tạo giao diện để tạo các ô, hãy kiểm tra xem bạn đã đặt Mã định danh trong Trình kiểm tra chưa. Sau đó kiểm tra xem nó có giống nhau không khi gọi dequeueReabilitiesCellWithIdentifier.

Tôi đã vô tình quên đặt một số định danh trong một dự án nặng bảng và sự thay đổi hiệu suất giống như ngày và đêm.


8

Tải UITableViewCells từ XIBs tiết kiệm rất nhiều mã, nhưng thường dẫn đến tốc độ cuộn khủng khiếp (thực ra, đó không phải là XIB mà là việc sử dụng quá nhiều UIView gây ra điều này).

Tôi đề nghị bạn hãy xem cái này: Liên kết tham khảo


6

Đây là phương thức lớp mà tôi đã sử dụng để tạo các ô tùy chỉnh từ 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;
}

Sau đó, trong XIB, tôi đặt tên lớp và sử dụng lại định danh. Sau đó, tôi chỉ có thể gọi phương thức đó trong trình điều khiển xem của mình thay vì

[[UITableViewCell] alloc] initWithFrame:]

Nó đủ nhanh và được sử dụng trong hai ứng dụng vận chuyển của tôi. Nó đáng tin cậy hơn gọi điện [nib objectAtIndex:0], và trong suy nghĩ của tôi, ít nhất là đáng tin cậy hơn ví dụ của Stephan Burlot vì bạn được đảm bảo chỉ thu được một cái nhìn từ XIB đúng loại.


5

Giải pháp đúng là đây

- (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; 
    }

4

Tải lại NIB là tốn kém. Tốt hơn là tải nó một lần, sau đó khởi tạo các đối tượng khi bạn cần một ô. Lưu ý rằng bạn có thể thêm UIImageViews vào nib, thậm chí nhiều ô, bằng cách sử dụng phương thức này ("registerNIB" iOS5 của Apple chỉ cho phép một đối tượng cấp cao nhất - Bug 10580062 "Bảng iOS5 Đăng kýNib: hạn chế quá mức"

Vì vậy, mã của tôi ở bên dưới - bạn đọc trong NIB một lần (khởi tạo như tôi đã làm hoặc trong viewDidload - bất cứ điều gì. Từ đó trở đi, bạn khởi tạo ngòi vào các đối tượng sau đó chọn cái bạn cần. Điều này hiệu quả hơn nhiều so với tải ngòi hơn và hơn

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;
}

4

Kiểm tra điều này - http://eppz.eu/blog/custom-uitableview-cell/ - cách thực sự thuận tiện bằng cách sử dụng một lớp nhỏ kết thúc một dòng trong triển khai bộ điều khiển:

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

nhập mô tả hình ảnh ở đây


3

Cách chính xác để làm điều đó là tạo một triển khai, tiêu đề và XIB của lớp con UITableViewCell. Trong XIB, xóa mọi khung nhìn và chỉ cần thêm một ô bảng. Đặt lớp làm tên của lớp con UITableViewCell. Đối với chủ sở hữu tệp, đặt tên lớp của lớp con UITableViewControll. Kết nối chủ sở hữu tệp với ô bằng cách sử dụng ổ cắm bảngViewCell.

Trong tệp tiêu đề:

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

Trong tệp thực hiện:

@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;
}

3

Những gì tôi làm cho điều này là khai báo một IBOutlet UITableViewCell *celltrong lớp điều khiển của bạn. Sau đó gọi NSBundle loadNibNamedphương thức lớp, nó sẽ cung cấp choUITableViewCell cho ô được khai báo ở trên.

Đối với xib tôi sẽ tạo một xib trống và thêm UITableViewCellđối tượng trong IB nơi nó có thể được thiết lập khi cần thiết. Khung nhìn này sau đó được kết nối với ô IBOutlettrong lớp trình điều khiển.

- (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 bổ sung loadNibNamed (đăng nhập ADC)

Bài viết cocoawithlove.com Tôi đã lấy ý tưởng từ (lấy ứng dụng mẫu số điện thoại)


3
  1. Tạo lớp AbcViewCellcon tùy chỉnh của riêng bạn từ UITableViewCell(Đảm bảo tên tệp lớp và tên tệp nib của bạn giống nhau)

  2. Tạo phương thức lớp mở rộng này.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
  3. Sử dụng nó.

    let cell: AbcViewCell = UITableViewCell.fromNib()


2

Đầu tiên nhập tệp ô tùy chỉnh của bạn #import "CustomCell.h"và sau đó thay đổi phương thức ủy nhiệm như được đề cập dưới đây:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

     return cell;
}

2

Trong Swift 4.2 và Xcode 10

Tôi có ba tệp di động XIB

trong ViewDidLoad đăng ký các tệp XIB của bạn như thế này ...

Đây là cách tiếp cận đầu tiên

tableView.register(UINib.init(nibName: "XIBCell", bundle: nil), forCellReuseIdentifier: "cell1")
tableView.register(UINib.init(nibName: "XIBCell2", bundle: nil), forCellReuseIdentifier: "cell2")
//tableView.register(UINib.init(nibName: "XIBCell3", bundle: nil), forCellReuseIdentifier: "cell3")

Cách tiếp cận thứ hai trực tiếp đăng ký tập tin XIB trong cellForRowAt indexPath:

Đây là chức năng ủy nhiệm bàn của tôi

//MARK: - Tableview delegates
override func numberOfSections(in tableView: UITableView) -> Int {

    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 6
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //This is first approach
    if indexPath.row == 0 {//Load first XIB cell
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! XIBCell
        return placeCell
    //Second approach
    } else if indexPath.row == 5 {//Load XIB cell3
        var cell = tableView.dequeueReusableCell(withIdentifier:"cell3") as? XIBCell3
        if cell == nil{
            let arrNib:Array = Bundle.main.loadNibNamed("XIBCell3",owner: self, options: nil)!
            cell = arrNib.first as? XIBCell3
        }

        //ADD action to XIB cell button
        cell?.btn.tag = indexPath.row//Add tag to button
        cell?.btn.addTarget(self, action: #selector(self.bookbtn1(_:)), for: .touchUpInside);//selector

        return cell!
    //This is first approach
    } else {//Load XIB cell2
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell2") as! XIBCell2

        return placeCell
    }

}

1

Đây là phương pháp của tôi cho việc đó: Tải UITableViewCells tùy chỉnh từ các tệp XIB Còn một phương thức khác

Ý tưởng là tạo một lớp con SampleCell của UITableViewCellmột thuộc IBOutlet UIView *contenttính và một thuộc tính cho mỗi khung nhìn phụ tùy chỉnh mà bạn cần cấu hình từ mã. Sau đó, để tạo một tệp SampleCell.xib. Trong tệp nib này, thay đổi chủ sở hữu tệp thành SampleCell. Thêm một nội dung UIViewcó kích thước phù hợp với nhu cầu của bạn. Thêm và định cấu hình tất cả các bản xem trước (nhãn, chế độ xem hình ảnh, nút, v.v.) bạn muốn. Cuối cùng, liên kết chế độ xem nội dung và các cuộc phỏng vấn với chủ sở hữu tệp.


1

Đây là một cách tiếp cận phổ biến để đăng ký các ô trong UITableView:

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

extension UITableViewCell: Reusable { }

extension UITableView {

func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
    let bundle = Bundle(for: cellClass.self)
    if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
        let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
        register(nib, forCellReuseIdentifier: cellClass.reuseID)
    } else {
        register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
    }
}

Giải trình:

  1. Reusablegiao thức tạo ID di động từ tên lớp của nó. Hãy chắc chắn rằng bạn tuân theo quy ước : cell ID == class name == nib name.
  2. UITableViewCellphù hợp với Reusablegiao thức.
  3. UITableView phần mở rộng trừu tượng hóa sự khác biệt trong việc đăng ký các ô thông qua ngòi hoặc lớp.

Ví dụ sử dụng:

override func viewDidLoad() {
    super.viewDidLoad()
    let tableView = UITableView()
    let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
    cellClasses.forEach(tableView.register)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
    ...
    return cell
}

0

Tôi không biết có cách nào hợp quy không, nhưng đây là phương pháp của tôi:

  • Tạo xib cho ViewContoder
  • Đặt lớp Chủ sở hữu tệp thành UIViewControll
  • Xóa chế độ xem và thêm UITableViewCell
  • Đặt Class của UITableViewCell thành lớp tùy chỉnh của bạn
  • Đặt Mã định danh của UITableViewCell của bạn
  • Đặt ổ cắm của chế độ xem trình điều khiển chế độ xem của bạn thành UITableViewCell

Và sử dụng mã này:

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

Trong ví dụ của bạn, sử dụng

[nib objectAtIndex:0]

có thể bị hỏng nếu Apple thay đổi thứ tự các mặt hàng trong xib.


Đối với tôi, điều này dẫn đến việc tạo ra một thể hiện mới luôn. Dequeue dường như trở lại con số không mỗi lần.
kỳ lạ

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;

0

Tiện ích mở rộng này yêu cầu 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
    }
}

Tạo một tệp Xib chỉ chứa 1 UITableViewCell tùy chỉnh.

Tải nó.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")

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

            let cellReuseIdentifier = "collabCell"
            var cell:collabCell! = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? collabCell
            if cell == nil {
                tableView.register(UINib(nibName: "collabCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
                cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! collabCell!
            }


            return cell

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.