คุณโหลด UITableViewCells ที่กำหนดเองจากไฟล์ Xib ได้อย่างไร


293

คำถามนั้นง่าย: คุณจะโหลด custom UITableViewCellจากไฟล์ Xib ได้อย่างไร การทำเช่นนั้นช่วยให้คุณใช้ตัวสร้างส่วนต่อประสานเพื่อออกแบบเซลล์ของคุณ เห็นได้ชัดว่าคำตอบนั้นไม่ง่ายเนื่องจากปัญหาการจัดการหน่วยความจำ เธรดนี้กล่าวถึงปัญหาและแนะนำวิธีแก้ไข แต่เป็นการนำเสนอ 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สำหรับส่วนประกอบที่คุณต้องการ จากนั้นสร้างไฟล์ "Empty XIB" ใหม่ เปิดไฟล์ Xib ใน IB เพิ่มUITableViewCellวัตถุตั้งค่าตัวระบุเป็น "MyCellIdentifier" และตั้งค่าระดับเป็น MyCell และเพิ่มส่วนประกอบของคุณ ในที่สุดเชื่อมต่อIBOutletsกับส่วนประกอบ โปรดทราบว่าเราไม่ได้ตั้งค่าเจ้าของไฟล์ใน IB

วิธีการอื่น ๆ สนับสนุนการตั้งค่าเจ้าของไฟล์และเตือนการรั่วไหลของหน่วยความจำหากไม่ได้โหลด Xib ผ่านคลาสโรงงานเพิ่มเติม ฉันทดสอบด้านบนภายใต้ Instruments / Leaks และไม่เห็นหน่วยความจำรั่ว

ดังนั้นวิธีบัญญัติของโหลดเซลล์จาก Xibs คืออะไร? เราตั้งเจ้าของไฟล์หรือไม่? เราต้องการโรงงานหรือไม่ ถ้าเป็นเช่นนั้นรหัสสำหรับโรงงานดูเหมือนเป็นอย่างไร หากมีการแก้ปัญหาหลายวิธีขอชี้แจงข้อดีและข้อเสียของแต่ละข้อ ...


2
มีใครบ้างที่สามารถแก้ไขหัวเรื่องเพื่อถามคำถามได้เช่น "คุณจะโหลด UITableViewCells ที่กำหนดเองจากไฟล์ Xib ได้อย่างไร" (ถ้าเพียงแค่นี้เป็นไปไม่ได้ใน StackOverflow.)
สตีเว่นฟิชเชอร์

1
สำหรับ iOS 5 ขึ้นไปนี่เป็นวิธีแก้ปัญหา: stackoverflow.com/questions/15591364/…ซึ่งเป็นวิธีเดียวกับโซลูชันของ giuseppe
Matt Becker

บันทึกย่อแบบง่าย ๆ (2013 สภาพแวดล้อม) ตอบได้ที่นี่stackoverflow.com/questions/15378788// jamihash
Fattie

คำตอบ:


288

นี่สองวิธีซึ่งเป็นรัฐผู้เขียนต้นฉบับถูกแนะนำโดยวิศวกร IB

ดูโพสต์จริงสำหรับรายละเอียดเพิ่มเติม ฉันชอบวิธีที่ # 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;
}

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

1
สำหรับวิธีที่ 1 คุณไม่ควรทำอะไรเช่น "cell = (BDCustomCell *) [[temporaryController.view keep] autorelease];" ดังนั้นเซลล์จะไม่ถูกปล่อยออกมาเมื่อมีการปล่อยตัวควบคุมชั่วคราว
Tod Cunningham

ฮึ่ม เอกสารที่พูดถึง # 2 ยังบอกให้คุณตั้งเจ้าของของเซลล์ในไฟล์ XIB ไปยังคลาสคอนโทรลเลอร์ที่รู้จัก อาจไม่สำคัญเมื่อคุณตั้งเจ้าของระหว่างการโหลด
ออสการ์

@OscarGoldman เจ้าของเซลล์ในไฟล์ XIB เป็นคลาส (เช่นประเภทของเจ้าของ) เจ้าของเซลล์ใน loadNibNamed: owner: options: เป็นวัตถุประเภทที่ระบุใน XIB
bentford

2
@CoolDocMan ตัวเลือก # 2 ยังคงใช้งานได้ ปัญหาน่าจะเกิดจากปลายปากกา นี่คือตัวอย่าง: github.com/bentford/NibTableCellExample
bentford

2
ทำไมรหัสเก่าสุดนี้ถึงสูงมาก Stackoverflow ทำอะไรสักอย่าง: /
Nico S.

304

ทางออกที่ถูกต้องคือ:

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

จะไปทำลายแอป iOS5 หรือไม่ ฉันไม่เคยเห็น UINib อย่างแท้จริง
Adam Waite

@AdamWaite การลงทะเบียนไฟล์ NIB ใช้งานได้กับ iOS 5 และใหม่กว่าดังนั้นจึงไม่ทำให้แอพ iOS 5 แตก และ UINib ยังคงมีอยู่ตั้งแต่ iOS 4
Mecki

เพื่อเป็นตัวอย่างที่ดีตรวจสอบ git repo ที่อ้างอิงในคำตอบยอดนิยมที่นี่: stackoverflow.com/questions/18746929/…
netigger

39

สมัครสมาชิก

หลังจาก iOS 7 กระบวนการนี้ง่ายลง ( 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")

( หมายเหตุ ) สิ่งนี้สามารถทำได้โดยการสร้างเซลล์ใน.xibหรือ.stroyboardไฟล์เป็นเซลล์ต้นแบบ หากคุณต้องการแนบชั้นเรียนกับพวกเขาคุณสามารถเลือกเซลล์ต้นแบบและเพิ่มระดับที่สอดคล้องกัน (ต้องเป็นลูกหลานUITableViewCellของแน่นอน)

dequeue

และหลังจากนั้นถอนการใช้ ( 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
}

ความแตกต่างที่ว่าวิธีการใหม่นี้ไม่เพียง แต่ทำให้เซลล์ dequeues แต่ยังสร้างหากไม่มีอยู่ (ซึ่งหมายความว่าคุณไม่ต้องทำif (cell == nil)shenanigans) และเซลล์พร้อมที่จะใช้เช่นเดียวกับในตัวอย่างข้างต้น

( คำเตือน ) tableView.dequeueReusableCell(withIdentifier:for:)มีพฤติกรรมใหม่ถ้าคุณเรียกอีกอันหนึ่ง (ไม่มีindexPath:) คุณจะได้รับพฤติกรรมเก่าซึ่งคุณต้องตรวจสอบnilและอินสแตนซ์ด้วยตนเองสังเกตเห็นUITableViewCell?ค่าที่ส่งคืน

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

และแน่นอนว่าประเภทของคลาสที่เชื่อมโยงของเซลล์นั้นเป็นประเภทที่คุณกำหนดไว้ในไฟล์. xib สำหรับUITableViewCellคลาสย่อยหรืออีกวิธีหนึ่งโดยใช้วิธีการลงทะเบียนอื่น ๆ

องค์ประกอบ

ตามหลักการแล้วเซลล์ของคุณได้รับการกำหนดค่าไว้แล้วในแง่ของลักษณะและการวางตำแหน่งเนื้อหา (เช่นป้ายกำกับและมุมมองรูปภาพ) ตามเวลาที่คุณลงทะเบียนและในcellForRowAtIndexPathวิธีการที่คุณกรอก

ทั้งหมดเข้าด้วยกัน

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

และแน่นอนทั้งหมดนี้มีอยู่ใน ObjC ด้วยชื่อเดียวกัน


นี่คือรุ่น ObjC:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
Zeb

33

คำตอบของ Took 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"];

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ของคุณในloadNibNamedคำสั่งคุณตั้งค่าเป็นไฟล์ของเจ้าของของคุณUITableViewControllerUITableViewCell

หากคุณลากและวางไปที่ไฟล์ส่วนหัวใน 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แทนที่จะไปที่เจ้าของไฟล์:

  1. คลิกขวาที่เจ้าของไฟล์เพื่อดึงรายการการเชื่อมต่อ
  2. ถ่ายภาพหน้าจอด้วย Command-Shift-4 (ลากเพื่อเลือกพื้นที่ที่จะจับ)
  3. x ออกการเชื่อมต่อจากเจ้าของไฟล์
  4. คลิกขวาที่ UITableCell ในลำดับชั้นวัตถุและเพิ่มการเชื่อมต่อใหม่

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

@xuhuanze ฉันแนะนำให้ถ่ายภาพหน้าจอเพื่อให้คุณได้บันทึกสิ่งที่เจ้าของไฟล์เชื่อมต่ออยู่แล้ว จากนั้นคุณสามารถสร้างการเชื่อมต่อเดียวกันเหล่านั้นได้อีกครั้ง คุณต้องลากและวางเพื่อเพิ่มการเชื่อมต่อไม่ใช่แค่คลิกเดียว
funroll

ขอบคุณมากฉันมี "คลาสนี้ไม่ใช่ค่าคีย์ตามมาตรฐานการเข้ารหัสสำหรับปัญหา" และแก้ไขโดยความช่วยเหลือของคุณ ฉันต้องการบอกคนอื่นว่าคุณควรเปลี่ยนคลาสของ UITableViewCell ของคุณเป็นคลาสของคุณซึ่งคุณใช้เป็นคลาสเซลล์แบบกำหนดเอง
เดนิส Kutlubaev

14

ฉันตัดสินใจที่จะโพสต์เนื่องจากฉันไม่ชอบคำตอบใด ๆ เหล่านี้ - สิ่งต่าง ๆ อาจจะง่ายกว่าและนี่เป็นวิธีที่กระชับที่สุดที่ฉันพบ

1. สร้าง Xib ของคุณในตัวสร้างส่วนต่อประสานตามที่คุณต้องการ

  • ตั้งเจ้าของไฟล์ให้เป็นคลาส NSObject
  • เพิ่ม UITableViewCell และตั้งค่าคลาสเป็น MyTableViewCellSubclass - ถ้า IB ของคุณล่ม (เกิดขึ้นใน Xcode> 4 เป็นการเขียนนี้) เพียงแค่ใช้ UIView ทำอินเทอร์เฟซใน Xcode 4 ถ้าคุณยังคงวางมันไว้
  • จัดวางภาพย่อยของคุณภายในเซลล์นี้และแนบการเชื่อมต่อ IBOutlet ของคุณกับ @interface ของคุณใน. h หรือ. m (.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;
}

9

หากคุณใช้ตัวสร้างส่วนต่อประสานเพื่อสร้างเซลล์ให้ตรวจสอบว่าคุณได้ตั้งค่าตัวระบุในตัวตรวจสอบแล้ว จากนั้นตรวจสอบว่ามันเหมือนกันเมื่อเรียก dequeueReusableCellWithIdentifier

ฉันลืมตั้งตัวระบุบางอย่างในโครงการที่มีตารางหนักและบังเอิญการเปลี่ยนแปลงประสิทธิภาพเป็นเหมือนกลางวันและกลางคืน


8

การโหลด UITableViewCells จาก XIBs จะบันทึกรหัสจำนวนมาก แต่โดยทั่วไปแล้วจะส่งผลให้ความเร็วในการเลื่อนที่แย่มาก (อันที่จริงมันไม่ใช่ XIB แต่เป็นการใช้งาน UIV วิวที่มากเกินไปซึ่งทำให้เกิดปัญหานี้)

ฉันขอแนะนำให้คุณดูที่: อ้างอิงลิงค์


6

นี่เป็นวิธีการเรียนที่ฉันใช้ในการสร้างเซลล์ที่กำหนดเองจาก XIBs:

+ (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:]

มันเร็วพอและมีการใช้งานในสองแอปพลิเคชันการจัดส่งของฉัน มันน่าเชื่อถือมากกว่าการโทร[nib objectAtIndex:0]และในใจของฉันอย่างน้อยก็น่าเชื่อถือกว่าตัวอย่างของสเตฟานเบอร์ล็อตเพราะคุณรับประกันได้ว่าจะได้รับมุมมองจาก XIB ที่เป็นประเภทที่ถูกต้องเท่านั้น


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

4

การรีโหลด NIB นั้นมีราคาแพง ดีกว่าที่จะโหลดครั้งเดียวแล้วยกตัวอย่างวัตถุเมื่อคุณต้องการเซลล์ โปรดทราบว่าคุณสามารถเพิ่ม UIImageViews และอื่น ๆ ให้กับปลายปากกาแม้กระทั่งหลายเซลล์โดยใช้วิธีนี้ (iOS "registerNIB" ของ iOS5 อนุญาตวัตถุระดับบนสุดเพียงชิ้นเดียว - Bug 10580062 "ตาราง iOS5 registerViewNib: จำกัด มากเกินไป"

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

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

ตรวจสอบสิ่งนี้ - http://eppz.eu/blog/custom-uitableview-cell/ - วิธีที่สะดวกมากในการใช้คลาสเล็ก ๆ ที่ลงท้ายด้วยหนึ่งบรรทัดในการใช้งานคอนโทรลเลอร์

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

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


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

3

สิ่งที่ฉันทำเพื่อสิ่งนี้คือการประกาศIBOutlet UITableViewCell *cellในคลาสควบคุมของคุณ จากนั้นเรียกใช้NSBundle loadNibNamedเมธอด class ซึ่งจะป้อนUITableViewCellไปยังเซลล์ที่ประกาศไว้ด้านบน

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

- (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 loadNibNamed (ล็อกอิน ADC)

บทความ cocoawithlove.com ฉันมาจากแนวคิด (รับแอพตัวอย่างหมายเลขโทรศัพท์)


3
  1. สร้างคลาสAbcViewCellย่อยคลาสที่คุณกำหนดเองจากUITableViewCell(ตรวจสอบให้แน่ใจว่าชื่อไฟล์คลาสและชื่อไฟล์เหมือนกัน)

  2. สร้างวิธีการเรียนส่วนขยายนี้

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
  3. ใช้มัน.

    let cell: AbcViewCell = UITableViewCell.fromNib()


2

ขั้นแรกให้นำเข้าไฟล์เซลล์ที่กำหนดเอง#import "CustomCell.h"จากนั้นเปลี่ยนวิธีมอบสิทธิ์ตามที่ระบุไว้ด้านล่าง:

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

ในSwift 4.2และ Xcode 10

ฉันมีไฟล์เซลล์ XIB สามไฟล์

ใน ViewDidLoad ลงทะเบียนไฟล์ XIB ของคุณเช่นนี้ ...

นี่เป็นวิธีแรก

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")

วิธีที่สองลงทะเบียนไฟล์ XIB โดยตรงในcellForRowAt indexPath:

นี่คือฟังก์ชั่นการมอบหมาย tableview ของฉัน

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

นี่คือวิธีการของฉัน: โหลด UITableViewCells ที่กำหนดเองจากไฟล์ XIB … แต่ยังมีวิธีอื่น

แนวคิดคือการสร้างคลาสย่อย SampleCell ของUITableViewCellด้วยIBOutlet UIView *contentคุณสมบัติและคุณสมบัติสำหรับแต่ละมุมมองย่อยที่กำหนดเองที่คุณต้องการกำหนดค่าจากรหัส จากนั้นเพื่อสร้างไฟล์ SampleCell.xib ในไฟล์ปลายปากกานี้ให้เปลี่ยนเจ้าของไฟล์เป็น SampleCell เพิ่มUIViewขนาดเนื้อหาให้เหมาะกับความต้องการของคุณ เพิ่มและกำหนดค่าการดูย่อยทั้งหมด (ฉลากมุมมองรูปภาพปุ่ม ฯลฯ ) ที่คุณต้องการ ขั้นสุดท้ายให้ลิงก์มุมมองเนื้อหาและมุมมองย่อยกับเจ้าของไฟล์


1

นี่เป็นวิธีการที่เป็นสากลสำหรับการลงทะเบียนเซลล์ใน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)
    }
}

คำอธิบาย:

  1. Reusableโปรโตคอลสร้าง ID เซลล์จากชื่อคลาส ให้แน่ใจว่าคุณปฏิบัติตามอนุสัญญา:cell ID == class name == nib name .
  2. UITableViewCellสอดคล้องกับReusableโปรโตคอล
  3. UITableView ส่วนขยายของนามธรรมทำให้ความแตกต่างในการลงทะเบียนเซลล์ผ่านทางปลายปากกาหรือชั้นเรียน

ตัวอย่างการใช้งาน:

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

ฉันไม่รู้ว่ามีวิธีบัญญัติหรือไม่ แต่นี่เป็นวิธีการของฉัน:

  • สร้าง xib สำหรับ ViewController
  • ตั้งค่าคลาสเจ้าของไฟล์เป็น UIViewController
  • ลบมุมมองและเพิ่ม UITableViewCell
  • ตั้งค่าคลาสของ UITableViewCell เป็นคลาสแบบกำหนดเองของคุณ
  • ตั้งค่า Identifier ของ 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


สำหรับฉันผลลัพธ์นี้จะสร้างอินสแตนซ์ใหม่เสมอ dequeue ดูเหมือนว่าจะกลับมาเป็นศูนย์ทุกครั้ง
แปลก

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

ส่วนขยายนี้ต้องการ 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
    }
}

สร้างไฟล์ Xib ที่มี UITableViewCell ที่กำหนดเองเพียง 1 รายการ

โหลดมัน

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

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