你如何从Xib文件加载自定义的UITableViewCells?

问题很简单:你如何从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一个新子类,并为您需要的组件添加IBOutlets 。 然后创建一个新的“Empty XIB”文件。 在IB中打开Xib文件,添加一个UITableViewCell对象,将其标识符设置为“MyCellIdentifier”,并将其类设置为MyCell并添加组件。 最后,将IBOutlets连接到组件。 请注意,我们没有在IB中设置文件所有者。

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

那么从Xibs加载单元格的规范方法是什么? 我们是否设置文件的所有者? 我们需要工厂吗? 如果是这样,工厂的代码是什么样的? 如果有多种解决方案,让我们来澄清他们每个人的优点和缺点......


原始作者陈述的两种方法是由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;
}

更新(2014年):方法#2仍然有效,但没有文档了。 它曾经在官方文档中,但现在已被删除以支持故事板。

我在Github上发布了一个工作示例:
https://github.com/bentford/NibTableCellExample


正确的解决方案是这样的:

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

寄存器

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 )出列:

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 (cell == nil) shenanigans不需要做),并且单元准备好使用就像在上面的例子中。

警告tableView.dequeueReusableCell(withIdentifier:for:)具有新的行为,如果你调用另一个(没有indexPath: tableView.dequeueReusableCell(withIdentifier:for:) ,你会得到旧行为,你需要检查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中都有相同的名称。

链接地址: http://www.djcxy.com/p/78227.html

上一篇: How do you load custom UITableViewCells from Xib files?

下一篇: How can I load vector image directly with iPhone SDK?