Tải UITableViewCell có thể tái sử dụng từ Nib


89

Tôi có thể thiết kế UITableViewCells tùy chỉnh và tải chúng tốt bằng cách sử dụng kỹ thuật được mô tả trong chuỗi được tìm thấy tại http://forums.macrumors.com/showthread.php?t=545061 . Tuy nhiên, việc sử dụng phương pháp đó không còn cho phép bạn chèn ô bằng reuseIdentifier, nghĩa là bạn phải tạo các phiên bản hoàn toàn mới của mỗi ô trong mỗi lần gọi. Có ai đã tìm ra cách tốt để vẫn lưu vào bộ nhớ cache các loại ô cụ thể để sử dụng lại, nhưng vẫn có thể thiết kế chúng trong Interface Builder không?

Câu trả lời:


74

Chỉ cần triển khai một phương thức với chữ ký phương thức thích hợp:

- (NSString *) reuseIdentifier {
  return @"myIdentifier";
}

Thực hiện phương pháp này ở đâu?
Krishnan

2
Trong lớp con UITableViewCell của bạn. developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/…
DenTheMan

5
Điều này là rủi ro. Điều gì xảy ra nếu bạn có hai lớp con của lớp con ô của mình và sử dụng cả hai lớp này trong một chế độ xem bảng duy nhất? Nếu họ gửi lệnh gọi số nhận dạng tái sử dụng lên super bạn sẽ xếp hàng một ô không đúng loại .............. Tôi nghĩ bạn cần ghi đè phương thức reuseIdentifier, nhưng hãy để nó trả về một số nhận dạng được thay thế chuỗi.
SK9

3
Để chắc chắn nó là duy nhất, bạn có thể làm:return NSStringFromClass([self class]);
ivanzoid

119

Trên thực tế, vì bạn đang tạo ô trong Trình tạo giao diện, chỉ cần đặt số nhận dạng sử dụng lại ở đó:

IB_reuse_identifier

Hoặc nếu bạn đang chạy Xcode 4, hãy kiểm tra tab Trình kiểm tra thuộc tính:

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

(Chỉnh sửa: Sau khi XIB của bạn được tạo bởi XCode, nó có chứa một UIView trống, nhưng chúng tôi cần một UITableViewCell; vì vậy bạn phải xóa UIView theo cách thủ công và chèn một ô Table View. Tất nhiên, IB sẽ không hiển thị bất kỳ tham số UITableViewCell nào cho UIView.)


Tôi làm gì để đặt Số nhận dạng, nếu tôi sử dụng Ô đã tạo Nib này cho nhiều ô? Nó sẽ dẫn chúng ta đến hai ô có cùng số nhận dạng.
Krishnan

4
Đừng nghĩ về nó như một định danh duy nhất - hãy nghĩ về nó giống như một tên loại hơn.
Tim Keating

Tôi không thấy tùy chọn để đặt số nhận dạng thông qua trình tạo giao diện tích hợp trong Xcode 4.3.3. Tôi chắc chắn đang đặt lớp thành lớp con UITableViewCell của mình. Tôi chỉ thiếu nó, hay nó đã biến mất?
Tyler

3
Ok, tôi đã giải quyết được vấn đề của mình. Nếu bạn bắt đầu với một đối tượng UIView trong trình tạo giao diện (trong Xcode 4) và thay đổi lớp của nó thành UITableViewCell, bạn sẽ không nhận được các thuộc tính dành riêng cho ô như số nhận dạng sử dụng lại. Để có được điều đó, bạn phải bắt đầu với một xib trống và kéo vào một đối tượng ô bảng, sau đó sẽ có các thuộc tính dành riêng cho ô mà bạn có thể chỉnh sửa.
Tyler

1
@Krishnan Hãy nghĩ theo cách này - khi bạn tạo ô xem bảng với số nhận dạng X, bạn đang nói "Cho tôi một ô từ nhóm có nhãn X." Nếu pool tồn tại và có một ô trống trong đó, thì nó sẽ cung cấp cho bạn. Nếu không, nó tạo nhóm (nếu cần), sau đó đưa tin lên ô, gắn nhãn là "X", rồi giao nó cho bạn. Vì vậy, các ô CÓ THỂ là duy nhất - ví dụ: bạn có thể tạo một nhóm chỉ có một ô với một số nhận dạng cụ thể - nhưng thư viện sử dụng chiến lược giống như danh sách miễn phí để tránh cấp phát bộ nhớ / dealloc.
Tim Keating

66

Bây giờ, trong iOS 5 có một phương thức UITableView thích hợp cho việc đó:

- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier

10
Các câu trả lời khác trong chủ đề này, bao gồm cả câu trả lời được chấp nhận, chứa lời khuyên lỗi thời.
Kaelin Colclasure

cái này có tương thích ngược không? Ý tôi là nếu tôi phát triển một ứng dụng với SDK 5.0 và mục tiêu tối thiểu 4.0 thì ứng dụng đó có chạy trên các thiết bị chạy iOS 4.0 không?
Abolfoooud 21/12/12

1
Không, điều này không tương thích ngược, như bất kỳ API mới nào trong iOS 5.0.
marzapower

đã làm việc ví dụ về cách tích hợp cái này vào bộ điều khiển của bạn: mindfiresolutions.com/…
mblackwell8

47

Tôi không thể nhớ nơi tôi tìm thấy mã này ban đầu, nhưng nó hoạt động rất tốt cho tôi cho đến nay.

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

    static NSString *CellIdentifier = @"CustomTableCell";
    static NSString *CellNib = @"CustomTableCellView";

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

    // perform additional custom work...

    return cell;
}

Thiết lập trình tạo giao diện mẫu ...

văn bản thay thế


12

Hãy xem câu trả lời tôi đã đưa ra cho câu hỏi này:

Có thể thiết kế các lớp con NSCell trong Trình tạo giao diện không?

Không chỉ có thể thiết kế UITableViewCell trong IB mà còn là điều mong muốn bởi vì nếu không, tất cả việc nối dây thủ công và bố trí nhiều phần tử rất tẻ nhạt. Hiệu suất tốt miễn là bạn cẩn thận để làm cho tất cả các yếu tố mờ đi khi có thể. ID tái sử dụng được đặt trong IB cho các thuộc tính của UITableViewCell, sau đó bạn sử dụng ID tái sử dụng phù hợp trong mã khi cố gắng hủy hàng.

Tôi cũng đã nghe từ một số người thuyết trình tại WWDC năm ngoái rằng bạn không nên tạo ô xem bảng trong IB, nhưng đó là một đống rác.


2
Bạn không nên tạo ô xem bảng trong IB nếu bạn cần tính minh bạch và muốn hiệu suất cuộn tốt. Đối với một số giao diện người dùng nhất định, bạn cần sự trong suốt (để hiển thị văn bản trên đồ họa, ví dụ). Đôi khi cách duy nhất để cuộn tốt trên phần cứng cũ hơn (trước A4) là hiển thị bằng mã, để tránh GPU phải kết hợp nhiều lớp trong suốt.
Nick Forge

2
Đúng, nhưng ngay cả với điều đó, bạn có thể nên để hiệu suất giảm nhẹ trên các thiết bị cũ để dễ bảo trì hơn các ô được tích hợp sẵn IB. Bạn cũng có thể sử dụng kỹ thuật này như một ô mẫu mà sau đó bạn vẽ các phần tử để tránh kết hợp với một phương pháp vẽ tùy chỉnh.
Kendall Helmstetter Gelner


6

Đây là một tùy chọn khác:

NSString * cellId = @"reuseCell";  
//...
NSArray * nibObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:nil options:nil];

for (id obj in nibObjects)
{
    if ([obj isKindOfClass:[CustomTableCell class]])
    {
        cell = obj;
        [cell setValue:cellId forKey:@"reuseIdentifier"];
        break;
    }
}

Lưu ý rằng đây là giải pháp duy nhất được đăng cho đến nay không yêu cầu phân lớp tùy chỉnh của bạn UITableViewCellđể đặt giá trị duy nhất cho reuseIdentifer. Tôi nghĩ rằng đây là những gì op ban đầu thực sự đang tìm kiếm.
charshep

Tôi hy vọng Apple sẽ không từ chối ứng dụng của tôi vì đã sử dụng cái này ... Tôi đang sử dụng cái này để lấy các ô "tĩnh" vì trong chế độ xem bảng của tôi, quá trình lấp đầy một ô diễn ra rất chậm. Bằng cách này, tôi chỉ phải thực thi nó một lần (đưa ra một số nhận dạng khác nhau cho mỗi hàng). Cảm ơn bạn!
Ricard Pérez del Campo

Rất hữu ích vì không có phương thức UITableViewCell nào để thiết lập điều này theo cách thủ công!
Jesse

2

Tôi tạo ô dạng xem tùy chỉnh của mình theo cách tương tự - ngoại trừ tôi kết nối ô thông qua IBOutlet.

Các [nib objectAt...] pháp này dễ bị thay đổi vị trí của các mục trong mảng.

Các UIViewController tiếp cận là tốt - chỉ cần thử nó, và nó hoạt động đủ tốt.

NHƯNG...

Trong mọi trường hợp, initWithStyle tạo KHÔNG được gọi, vì vậy không có khởi tạo mặc định nào được thực hiện.

Tôi đã đọc nhiều nơi khác nhau về việc sử dụng initWithCoderhoặcawakeFromNib , nhưng không có bằng chứng thuyết phục nào cho thấy một trong hai cách này là đúng.

Ngoài việc gọi rõ ràng một số phương thức khởi tạo trong cellForRowAtIndexPathphương thức, tôi vẫn chưa tìm thấy câu trả lời cho điều này.


AwakeFromNib là cách phù hợp để phản ứng với một đối tượng đang được tải từ NIB.
Jon Hess

2

Một thời gian trước, tôi đã tìm thấy một bài đăng trên blog tuyệt vời về chủ đề này tại blog.atebits.com và từ đó bắt đầu sử dụng lớp Loren Brichter ABTableViewCell để thực hiện tất cả các UITableViewCell của tôi.

Bạn kết thúc với một UIView vùng chứa đơn giản để đặt tất cả các vật dụng của mình và cuộn nhanh như chớp.

Hy vọng điều này là hữu ích.


2

Kỹ thuật này cũng hoạt động và không yêu cầu một ivar sôi nổi trong bộ điều khiển chế độ xem của bạn để quản lý bộ nhớ. Ở đây, ô xem bảng tùy chỉnh nằm trong một xib có tên "CustomCell.xib".

 static NSData *sLoadedCustomCell = nil;

 cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
 if (cell == nil) 
 {
   if (sLoadedCustomCell == nil) 
   {        
      // Load the custom table cell xib
      // and extract a reference to the cell object returned
      // and cache it in a static to avoid reloading the nib again.

      for (id loadedObject in [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:nil options:nil]) 
      {
        if ([loadedObject isKindOfClass:[UITableViewCell class]]) 
        {
          sLoadedCustomCell = [[NSKeyedArchiver archivedDataWithRootObject: loadedObject] retain];
          break;
        }
    }
    cell = (UITableViewCell *)[NSKeyedUnarchiver unarchiveObjectWithData: sLoadedCustomCell];
  }

1
Việc lưu trữ và hủy lưu trữ là hoàn toàn không cần thiết.
Bryan Henry

1
Lưu trữ / hủy lưu trữ là không cần thiết nếu bạn ổn với việc tải ô từ nib của nó bất cứ khi nào nó không thể được xếp lại. Tuy nhiên, nếu bạn chỉ muốn tải ô từ ngòi của nó đúng một lần , thì bạn cần phải lưu nó vào bộ nhớ đệm. Tôi hoàn thành bộ nhớ đệm đó bằng cách sử dụng NSKeyedArchiving vì UITableViewCell không triển khai NSCopying.
Bill Garrison

1
Tất cả những gì đã nói, việc sử dụng UINib để tải ô sẽ đạt được hiệu quả tương tự: tải từ đĩa một lần, tải từ bộ nhớ sau đó.
Bill Garrison

2

Phương pháp Louis đã làm việc cho tôi. Đây là mã tôi sử dụng để tạo UITableViewCell từ nib:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"CustomCellId"];

    if (cell == nil) 
    {
        UIViewController *c = [[UIViewController alloc] initWithNibName:@"CustomCell" bundle:nil];
        cell = (PostCell *)c.view;
        [c release];
    }

    return cell;
}

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

1

Giải pháp gustavogb không hoạt động với tôi, những gì tôi đã thử là:

ChainesController *c = [[ChainesController alloc] initWithNibName:@"ChainesController" bundle:nil];
[[NSBundle mainBundle] loadNibNamed:@"ChaineArticleCell" owner:c options:nil];
cell = [c.blogTableViewCell retain];
[c release];

Nó dường như hoạt động. BlogTableViewCell là IBOutlet cho ô và ChainesController là chủ sở hữu của tệp.


1

Từ tài liệu UITableView liên quan đến dequeueWithReuseIdentifier: "Chuỗi xác định đối tượng ô sẽ được sử dụng lại. Theo mặc định, mã định danh của ô có thể sử dụng lại là tên lớp của nó, nhưng bạn có thể thay đổi nó thành bất kỳ giá trị tùy ý nào."

Ghi đè -reuseIdentifer bản thân bạn là một rủi ro. Điều gì xảy ra nếu bạn có hai lớp con của lớp con ô của mình và sử dụng cả hai lớp này trong một chế độ xem bảng duy nhất? Nếu họ gửi lệnh gọi số nhận dạng tái sử dụng lên super bạn sẽ xếp hàng một ô không đúng loại .............. Tôi nghĩ bạn cần ghi đè phương thức reuseIdentifier, nhưng hãy để nó trả về một số nhận dạng được thay thế chuỗi. Hoặc, nếu chưa được chỉ định, hãy trả về lớp dưới dạng chuỗi.


0

Về giá trị của nó, tôi đã hỏi một kỹ sư iPhone về điều này tại một trong những cuộc nói chuyện về iPhone Tech. Câu trả lời của anh ấy là, "Có, có thể sử dụng IB để tạo tế bào. Nhưng đừng. Làm ơn, đừng."


1
Thật ki quặc. Ít nhất hai trong số các cuộc thảo luận tại cuộc nói chuyện tại NY có mã demo sử dụng các ô được tạo trong IB.
Shawn Craver

3
Tôi không chắc tôi tin điều này vì họ sử dụng IB để tạo các ô trong dự án ví dụ về Ô bảng xem nâng cao của Apple.
iwasrobbed

Cảm ơn vì điều đó. Mỗi khi tôi làm điều đó, tôi đều gặp phải vấn đề. Đây có thể là lý do tại sao
skorulis 23/10/10

Anh ta có lẽ không có đủ kiến ​​thức về nó.
aryaxt

0

Tôi đã làm theo hướng dẫn của Apple do Ben Mosher liên kết (cảm ơn!) Nhưng nhận thấy rằng Apple đã bỏ qua một điểm quan trọng. Đối tượng họ thiết kế trong IB chỉ là một UITableViewCell, cũng như biến mà họ tải từ nó. Nhưng nếu bạn thực sự thiết lập nó như một lớp con tùy chỉnh của UITableViewCell và viết các tệp mã cho lớp con, bạn có thể viết khai báo IBOutlet và phương thức IBAction trong mã và chuyển chúng thành các phần tử tùy chỉnh của bạn trong IB. Sau đó, không cần sử dụng thẻ xem để truy cập các phần tử này và bạn có thể tạo bất kỳ loại ô điên nào bạn muốn. Đó là thiên đường Cocoa Touch.

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.