问题很简单:如何从Xib文件加载自定义UITableViewCell ?这样做允许您使用Interface Builder来设计单元格。由于内存管理问题,答案显然并不简单。这个帖子提到了这个问题,并提出了一个解决方案,但是在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的一个新子类,并为您想要的组件添加iboutlet。然后创建一个新的“Empty XIB”文件。在IB中打开Xib文件,添加一个UITableViewCell对象,将其标识符设置为“MyCellIdentifier”,并将其类设置为MyCell并添加组件。最后,将iboutlet连接到组件。注意,我们没有在IB中设置文件的所有者。

其他方法主张设置文件的所有者,如果Xib没有通过额外的工厂类加载,则警告内存泄漏。我在Instruments/Leaks下测试了上面的内容,没有发现内存泄漏。

那么从xib加载单元格的规范方法是什么呢?我们设置文件的所有者吗?我们需要工厂吗?如果是,工厂的代码是什么样子的?如果有多种解决方案,让我们来阐明每一种方案的优缺点。


当前回答

如果您正在使用Interface Builder来创建单元格,请检查是否在检查器中设置了标识符。然后在调用dequeueReusableCellWithIdentifier时检查是否相同。

我无意中忘记在一个表较多的项目中设置一些标识符,性能变化就像白天和黑夜一样。

其他回答

检查这个- http://eppz.eu/blog/custom-uitableview-cell/ -非常方便的方式使用一个小类,在控制器实现中结束一行:

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

下面是我一直用于从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:]

它已经足够快了,并且在我的两个运输应用程序中使用。它比调用[nib objectAtIndex:0]更可靠,至少在我看来,比Stephan Burlot的示例更可靠,因为可以保证只从正确类型的XIB中获取视图。

注册

在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的后代)。

出列

然后,使用(swift 3.0) dequeued:

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

    cell.textLabel?.text = "Hello"

    return cell
}

不同之处在于,这个新方法不仅解除了单元格的队列,它还创建了if不存在(这意味着你不必做if (cell == nil)恶作剧),并且单元格已经准备好使用了,就像上面的例子一样。

(警告)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中都有相同的名字。

正确的解决方法是:

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

如果您正在使用Interface Builder来创建单元格,请检查是否在检查器中设置了标识符。然后在调用dequeueReusableCellWithIdentifier时检查是否相同。

我无意中忘记在一个表较多的项目中设置一些标识符,性能变化就像白天和黑夜一样。