问题很简单:如何从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加载单元格的规范方法是什么呢?我们设置文件的所有者吗?我们需要工厂吗?如果是,工厂的代码是什么样子的?如果有多种解决方案,让我们来阐明每一种方案的优缺点。
这里有两种方法,原作者说,是由IB工程师推荐的。
更多细节请参见实际文章。我更喜欢第二种方法,因为它看起来更简单。
方法# 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
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
}
为此,我在控制器类中声明了一个IBOutlet UITableViewCell *单元格。
然后调用NSBundle loadNibNamed类方法,它将把UITableViewCell提供给上面声明的单元格。
对于xib,我将创建一个空的xib,并在IB中添加UITableViewCell对象,它可以根据需要进行设置。然后,该视图连接到控制器类中的单元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文章我的概念来源(获取电话号码示例应用程序)
我使用了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's Owner。
通过在loadNibNamed语句中传递owner:self,你将UITableViewController设置为UITableViewCell的文件所有者。
如果你拖拽到IB中的头文件来设置动作和出口,默认情况下它会将它们设置为file 's Owner。
在loadNibNamed:owner:options中,苹果的代码将尝试在你的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而不是File的Owner:
右键单击File's Owner调出连接列表
用Command-Shift-4进行屏幕捕捉(拖动以选择要捕捉的区域)
x输出来自文件所有者的连接
右键单击对象层次结构中的UITableCell并重新添加连接。