当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何从Xib文件加载自定义UITableViewCells?》经验,为你挑选了10个好方法。

问题很简单:如何UITableViewCell从Xib文件加载自定义?这样做允许您使用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.然后创建一个新的"空XIB"文件.在IB中打开Xib文件,添加一个UITableViewCell对象,将其标识符设置为"MyCellIdentifier",并将其类设置为MyCell并添加组件.最后,连接IBOutlets到组件.请注意,我们没有在IB中设置文件所有者.

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

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



1> giuseppe..:

正确的解决方案是:

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



2> bentford..:

以下是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


@CoolDocMan选项#2仍然有效.问题最有可能是笔尖.这是一个例子:https://github.com/bentford/NibTableCellExample
为什么这个超级老代码排名如此之高。Stackoverflow做一些事情:/

3> Can..:

寄存器

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)恶作剧),并且细胞可以像上面的示例一样使用.

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



4> 小智..:

拿起Shawn Craver的回答并清理了一下.

BBCell.h:

#import 

@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"];



5> funroll..:

我使用了bentford的方法#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文件中与File的所有者的连接.

通过传递owner:self你的loadNibNamed陈述,你可以设置你的UITableViewController文件的所有者UITableViewCell.

如果拖放到IB中的头文件以设置操作和出口,默认情况下会将其设置为文件所有者.

loadNibNamed:owner:options,Apple的代码将尝试设置您的属性UITableViewController,因为这是所有者.但是你没有在那里定义那些属性,所以你得到一个关于符合键值编码的错误:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[ 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

一个简单的解决方法是将Interface Builder连接指向UITableViewCell而不是File的所有者:

    右键单击File的Owner以获取连接列表

    使用Command-Shift-4进行屏幕捕获(拖动以选择要捕获的区域)

    x输出文件所有者的连接

    右键单击Object层次结构中的UITableCell并重新添加连接.



6> webstersx..:

我决定发帖,因为我不喜欢任何这些答案 - 事情总是更简单,这是迄今为止我发现的最简洁的方式.

1.根据需要在Interface Builder中构建Xib

将File的Owner设置为NSObject类

添加一个UITableViewCell并将其类设置为MyTableViewCellSubclass - 如果您的IB崩溃(在撰写本文时Xcode> 4中发生),只需使用UIView在Xcode 4中执行该接口,如果您仍然存在它

在此单元格内布置子视图,并将您的IBOutlet连接附加到.h或.m中的@interface(.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;
}



7> Alex R. Youn..:

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

我不小心忘了在表格繁重的项目中设置一些标识符,而且性能变化就像是白天和黑夜.



8> Can Berk Güd..:

从XIB加载UITableViewCells可以节省大量代码,但通常会导致可怕的滚动速度(实际上,它不是XIB,而是过度使用导致此问题的UIViews).

我建议你看看这个:链接参考



9> Shawn Craver..:

这是我用来从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中获取一个视图.



10> 小智..:

正确的解决方案就是这样

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

推荐阅读
惬听风吟jyy_802
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有