การโหลด UITableViewCell แบบใช้ซ้ำได้จาก Nib


89

ผมสามารถที่จะออกแบบ UITableViewCells กำหนดเองและโหลดพวกเขาเพียงแค่ปรับใช้เทคนิคที่อธิบายไว้ในหัวข้อที่พบในhttp://forums.macrumors.com/showthread.php?t=545061 อย่างไรก็ตามการใช้วิธีดังกล่าวไม่อนุญาตให้คุณเริ่มต้นเซลล์ด้วย reuseIdentifier อีกต่อไปซึ่งหมายความว่าคุณต้องสร้างอินสแตนซ์ใหม่ทั้งหมดของแต่ละเซลล์ทุกครั้งที่โทร มีใครคิดวิธีที่ดีในการแคชเซลล์บางประเภทเพื่อนำกลับมาใช้ใหม่ได้ แต่ยังสามารถออกแบบใน Interface Builder ได้หรือไม่?

คำตอบ:


74

เพียงใช้วิธีการที่มีลายเซ็นวิธีที่เหมาะสม:

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

จะใช้วิธีนี้ได้ที่ไหน?
Krishnan

2
ในคลาสย่อย UITableViewCell ของคุณ developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/…
DenTheMan

5
นี่คือความเสี่ยง จะเกิดอะไรขึ้นถ้าคุณมีคลาสย่อยของเซลล์สองคลาสและใช้ทั้งสองอย่างนี้ในมุมมองตารางเดียว หากพวกเขาส่งการเรียกตัวระบุการใช้ซ้ำไปยัง super คุณจะยกเลิกการจัดคิวเซลล์ที่ผิดประเภท .............. ฉันคิดว่าคุณต้องแทนที่เมธอด reuseIdentifier แต่ให้ส่งคืนตัวระบุที่ถูกแทนที่ สตริง
SK9

3
เพื่อให้แน่ใจว่าเป็นเอกลักษณ์คุณสามารถทำได้:return NSStringFromClass([self class]);
ivanzoid

119

อันที่จริงเนื่องจากคุณกำลังสร้างเซลล์ในตัวสร้างส่วนต่อประสานให้ตั้งค่าตัวระบุการใช้ซ้ำที่นั่น:

IB_reuse_identifier

หรือถ้าคุณกำลังเรียกใช้ Xcode 4 ให้ตรวจสอบแท็บตัวตรวจสอบคุณสมบัติ:

ป้อนคำอธิบายภาพที่นี่

(แก้ไข: หลังจาก XIB ของคุณสร้างโดย XCode แล้วจะมี UIView ว่างเปล่า แต่เราต้องการ UITableViewCell ดังนั้นคุณต้องลบ UIView ด้วยตนเองและแทรกเซลล์มุมมองตารางแน่นอน IB จะไม่แสดงพารามิเตอร์ UITableViewCell สำหรับ a UIView.)


ฉันจะตั้งค่าตัวระบุอะไรได้บ้างถ้าฉันใช้ Nib ที่สร้างเซลล์สำหรับเซลล์มากกว่าหนึ่งเซลล์ สิ่งนี้จะทำให้เรามีสองเซลล์ที่มีตัวระบุเดียวกัน
Krishnan

4
อย่าคิดว่ามันเป็นตัวระบุที่ไม่ซ้ำใครให้คิดว่ามันเหมือนชื่อประเภทมากกว่า
Tim Keating

ฉันไม่เห็นตัวเลือกในการตั้งค่าตัวระบุผ่านตัวสร้างอินเทอร์เฟซในตัวใน Xcode 4.3.3 ฉันตั้งค่าคลาสเป็นคลาสย่อย UITableViewCell ของฉันอย่างแน่นอน ฉันแค่คิดถึงมันหรือมันหายไป?
Tyler

3
โอเคฉันแก้ปัญหาได้แล้ว หากคุณเริ่มต้นด้วยออบเจ็กต์ UIView ในตัวสร้างอินเทอร์เฟซ (ภายใน Xcode 4) และเปลี่ยนคลาสเป็น UITableViewCell คุณจะไม่ได้รับคุณสมบัติเฉพาะเซลล์เช่นตัวระบุการใช้ซ้ำ เพื่อให้ได้สิ่งนั้นคุณควรเริ่มต้นด้วย xib ว่างและลากในวัตถุเซลล์ตารางซึ่งจะมีคุณสมบัติเฉพาะเซลล์ที่คุณสามารถแก้ไขได้
Tyler

1
@Krishnan ลองคิดแบบนี้ - เมื่อคุณสร้างเซลล์มุมมองตารางที่มีตัวระบุ X คุณกำลังพูดว่า "ขอเซลล์จากสระว่ายน้ำ X ให้ฉัน" หากสระว่ายน้ำมีอยู่และมีเซลล์ว่างอยู่ในนั้นก็จะมอบให้กับคุณ มิฉะนั้นจะสร้างพูล (ถ้าจำเป็น) จากนั้นแจ้งข้อมูลในเซลล์ติดป้ายกำกับว่า "X" แล้วส่งให้คุณ ดังนั้นเซลล์จึงไม่ซ้ำกัน - เช่นคุณสามารถสร้างพูลที่มีเพียงเซลล์เดียวที่มีตัวระบุเฉพาะ - แต่ไลบรารีใช้กลยุทธ์แบบรายการฟรีเพื่อหลีกเลี่ยงการจัดสรรหน่วยความจำ / dealloc
Tim Keating

66

ตอนนี้ใน iOS 5 มีวิธี UITableView ที่เหมาะสมสำหรับสิ่งนั้น:

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

10
คำตอบอื่น ๆ ในชุดข้อความนี้รวมถึงคำตอบที่ยอมรับมีคำแนะนำที่ล้าสมัย
Kaelin Colclasure

ย้อนหลังได้ไหม ฉันหมายถึงถ้าฉันพัฒนาแอพด้วย SDK 5.0 และกำหนดเป้าหมายขั้นต่ำ 4.0 แอพจะทำงานบนอุปกรณ์ที่มี iOS 4.0 เช่น?
Abolfoooud

1
ไม่สิ่งนี้ไม่สามารถใช้งานได้แบบย้อนหลังเหมือนกับ API ใหม่ใน iOS 5.0
marzapower

ตัวอย่างการทำงานของวิธีการรวมสิ่งนี้เข้ากับคอนโทรลเลอร์ของคุณ: mindfiresolutions.com/…
mblackwell8

47

ฉันจำไม่ได้ว่าตอนแรกพบรหัสนี้ที่ไหน แต่ตอนนี้ใช้งานได้ดีสำหรับฉัน

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

ตัวอย่างการตั้งค่าตัวสร้างอินเทอร์เฟซ ...

ข้อความแสดงแทน


12

ดูคำตอบที่ฉันให้สำหรับคำถามนี้:

เป็นไปได้ไหมที่จะออกแบบคลาสย่อย NSCell ใน Interface Builder

ไม่เพียง แต่เป็นไปได้ที่จะออกแบบ UITableViewCell ใน IB เท่านั้น แต่ยังเป็นที่พึงปรารถนาเพราะมิฉะนั้นการเดินสายและการจัดวางองค์ประกอบหลาย ๆ องค์ประกอบด้วยตนเองนั้นน่าเบื่อมาก การแสดงทำได้ดีตราบเท่าที่คุณระมัดระวังที่จะทำให้องค์ประกอบทั้งหมดทึบแสงเมื่อทำได้ reuseID ถูกตั้งค่าใน IB สำหรับคุณสมบัติของ UITableViewCell จากนั้นคุณใช้รหัสการใช้ซ้ำที่ตรงกันในรหัสเมื่อพยายามยกเลิกคิว

ฉันได้ยินจากผู้นำเสนอบางคนที่ WWDC เมื่อปีที่แล้วว่าคุณไม่ควรสร้างเซลล์มุมมองตารางใน IB แต่มันเป็นสองชั้น


2
คุณไม่ควรสร้างเซลล์มุมมองตารางใน IB หากคุณต้องการความโปร่งใสและต้องการประสิทธิภาพในการเลื่อนที่ดี สำหรับ UI บางรายการคุณต้องมีความโปร่งใส (เพื่อแสดงข้อความบนกราฟิกเช่น) บางครั้งวิธีเดียวที่จะทำให้การเลื่อนที่ดีบนฮาร์ดแวร์รุ่นเก่า (ก่อน A4) คือการแสดงผลในโค้ดเพื่อหลีกเลี่ยงไม่ให้ GPU ต้องประกอบเลเยอร์โปร่งใสหลายชั้น
Nick Forge

2
จริงอยู่ แต่ถึงอย่างนั้นคุณอาจจะดีกว่าที่จะปล่อยให้ประสิทธิภาพลดลงเล็กน้อยในอุปกรณ์เก่าเพื่อการบำรุงรักษาที่ง่ายขึ้นของเซลล์ที่สร้างขึ้นใน IB คุณยังสามารถใช้เทคนิคนี้เป็นเซลล์แม่แบบที่คุณวาดองค์ประกอบจากนั้นเพื่อหลีกเลี่ยงการประกอบด้วยวิธีการวาดแบบกำหนดเอง
Kendall Helmstetter Gelner

7

ใน iOS ประมาณ 4.0 มีคำแนะนำเฉพาะในเอกสาร iOS ที่ทำให้การทำงานนี้เร็วมาก:

http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7

เลื่อนลงไปที่ที่พูดถึงคลาสย่อย UITableViewCell


6

นี่คืออีกทางเลือกหนึ่ง:

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

ทราบว่านี้เป็นเพียงการแก้ปัญหาการโพสต์เพื่อให้ห่างไกลที่ไม่ต้องใช้ subclassing ของคุณเองเพื่อที่จะตั้งค่าที่ไม่ซ้ำกันUITableViewCell reuseIdentiferฉันคิดว่านี่คือสิ่งที่ op ดั้งเดิมกำลังมองหาอยู่
charshep

ฉันหวังว่า Apple จะไม่ปฏิเสธแอปพลิเคชันของฉันสำหรับการใช้สิ่งนี้ ... ฉันกำลังใช้สิ่งนี้เพื่อรับเซลล์ "คงที่" เนื่องจากในมุมมองตารางของฉันกระบวนการเติมเซลล์จะเงียบช้า วิธีนี้ฉันต้องดำเนินการครั้งเดียว (ให้ตัวระบุที่แตกต่างกันในแต่ละแถว) ขอขอบคุณ!
Ricard Pérez del Campo

มีประโยชน์มากเนื่องจากไม่มีวิธี UITableViewCell สำหรับการตั้งค่านี้ด้วยตนเอง!
เจสซี่

2

ฉันสร้างเซลล์มุมมองที่กำหนดเองในลักษณะที่คล้ายกันยกเว้นว่าฉันเชื่อมต่อเซลล์ผ่าน IBOutlet

[nib objectAt...]วิธีการเป็นที่ประทับใจกับการเปลี่ยนแปลงตำแหน่งของรายการในอาร์เรย์

UIViewControllerวิธีเป็นสิ่งที่ดี - เพียงแค่พยายามและการทำงานที่ดีพอ

แต่...

ในทุกกรณี initWithStyleจะไม่มีการเรียกตัวสร้างดังนั้นจึงไม่มีการเริ่มต้นเริ่มต้น

ฉันได้อ่านสถานที่ต่างๆเกี่ยวกับการใช้initWithCoderหรือawakeFromNibแต่ไม่มีหลักฐานแน่ชัดว่าสิ่งเหล่านี้เป็นวิธีที่ถูกต้อง

นอกเหนือจากการเรียกวิธีการเริ่มต้นอย่างชัดเจนในcellForRowAtIndexPathวิธีนี้ฉันยังไม่พบคำตอบสำหรับสิ่งนี้


AwakenFromNib เป็นวิธีที่ถูกต้องในการตอบสนองต่อวัตถุที่โหลดจาก NIB
Jon Hess

2

ในขณะที่กลับมาฉันพบบล็อกโพสต์ที่ยอดเยี่ยมในหัวข้อนี้ที่blog.atebits.comและตั้งแต่นั้นมาก็เริ่มใช้คลาส Loren Brichter ABTableViewCell เพื่อทำ UITableViewCells ทั้งหมดของฉัน

คุณจะจบลงด้วย UIView คอนเทนเนอร์ที่เรียบง่ายเพื่อวางวิดเจ็ตทั้งหมดของคุณและการเลื่อนก็ทำได้อย่างรวดเร็ว

หวังว่านี่จะเป็นประโยชน์


2

เทคนิคนี้ยังใช้งานได้และไม่ต้องใช้ ivar ที่ขี้ขลาดในตัวควบคุมมุมมองของคุณสำหรับการจัดการหน่วยความจำ ที่นี่เซลล์มุมมองตารางที่กำหนดเองจะอยู่ใน xib ชื่อ "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
การเก็บถาวรและการยกเลิกการเก็บเป็นสิ่งที่ไม่จำเป็นทั้งหมด
Bryan Henry

1
การเก็บถาวร / ยกเลิกการเก็บจะไม่จำเป็นหากคุณสามารถโหลดเซลล์จากปลายปากกาได้เมื่อใดก็ตามที่ไม่สามารถยกเลิกการจัดคิวได้ อย่างไรก็ตามหากคุณต้องการโหลดเซลล์จากปลายปากกาเพียงครั้งเดียวคุณต้องแคชในหน่วยความจำ ฉันทำแคชนั้นสำเร็จโดยใช้ NSKeyedArchiving เนื่องจาก UITableViewCell ไม่ได้ใช้ NSCopying
Bill Garrison

1
ทั้งหมดที่กล่าวมาการใช้ UINib เพื่อโหลดเซลล์จะได้ผลเช่นเดียวกันคือโหลดจากดิสก์ครั้งเดียวโหลดจากหน่วยความจำหลังจากนั้น
Bill Garrison

2

วิธีการของหลุยส์ใช้ได้ผลสำหรับฉัน นี่คือรหัสที่ฉันใช้เพื่อสร้าง UITableViewCell จากปลายปากกา:

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

วิธีแก้ปัญหา gustavogb ไม่ได้ผลสำหรับฉันสิ่งที่ฉันพยายามคือ:

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

ดูเหมือนว่าจะได้ผล blogTableViewCell เป็น IBOutlet สำหรับเซลล์และ ChainesController เป็นเจ้าของไฟล์


1

จากเอกสาร UITableView เกี่ยวกับdequeueWithReuseIdentifier: "สตริงที่ระบุอ็อบเจ็กต์เซลล์ที่จะใช้ซ้ำโดยค่าเริ่มต้นตัวระบุของเซลล์ที่ใช้ซ้ำได้คือชื่อคลาส แต่คุณสามารถเปลี่ยนเป็นค่าใดก็ได้"

การลบล้าง -reuseIdentifer ตัวเองมีความเสี่ยง จะเกิดอะไรขึ้นถ้าคุณมีคลาสย่อยของเซลล์สองคลาสและใช้ทั้งสองอย่างนี้ในมุมมองตารางเดียว หากพวกเขาส่งการเรียกตัวระบุการใช้ซ้ำไปยัง super คุณจะยกเลิกการจัดคิวเซลล์ที่ผิดประเภท .............. ฉันคิดว่าคุณต้องแทนที่เมธอด reuseIdentifier แต่ให้ส่งคืนตัวระบุที่ถูกแทนที่ สตริง หรือถ้ายังไม่ได้ระบุให้ส่งคืนคลาสเป็นสตริง


0

สำหรับสิ่งที่คุ้มค่าฉันถามวิศวกร iPhone เกี่ยวกับเรื่องนี้ที่หนึ่งใน iPhone Tech Talks คำตอบของเขาคือ "ใช่มันเป็นไปได้ที่จะใช้ IB เพื่อสร้างเซลล์ แต่อย่าได้โปรดอย่า"


1
นั่นเป็นเรื่องแปลก การพูดคุยอย่างน้อยสองครั้งใน NY talk มีรหัสสาธิตที่ใช้เซลล์ที่สร้างขึ้นใน IB
Shawn Craver

3
ไม่แน่ใจว่าฉันเชื่อสิ่งนี้เนื่องจากพวกเขาใช้ IB เพื่อสร้างเซลล์ในโครงการตัวอย่าง Advanced Table View Cells ของ Apple
iwasrobbed

ขอบคุณสำหรับสิ่งนั้น ทุกครั้งที่ฉันทำฉันประสบปัญหา นี่อาจเป็นเหตุผลว่าทำไม
skorulis

เขาคงไม่มีความรู้เรื่องนี้มากพอ
aryaxt

0

ฉันทำตามคำแนะนำของ Apple ตามที่ Ben Mosher เชื่อมโยง (ขอบคุณ!) แต่พบว่า Apple ละเว้นประเด็นสำคัญ วัตถุที่ออกแบบใน IB เป็นเพียง UITableViewCell เช่นเดียวกับตัวแปรที่โหลดจากมัน แต่ถ้าคุณตั้งค่าเป็นคลาสย่อยที่กำหนดเองของ UITableViewCell และเขียนไฟล์โค้ดสำหรับคลาสย่อยคุณสามารถเขียนการประกาศ IBOutlet และวิธี IBAction ในโค้ดและต่อเข้ากับองค์ประกอบที่กำหนดเองของคุณใน IB จากนั้นไม่จำเป็นต้องใช้แท็กมุมมองเพื่อเข้าถึงองค์ประกอบเหล่านี้และคุณสามารถสร้างเซลล์บ้าอะไรก็ได้ที่คุณต้องการ มันคือสวรรค์ของ Cocoa Touch

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.