如何从 Xib 文件加载自定义 UITableViewCell?
问题很简单:如何从 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
。 然后创建一个新的“空 XIB”文件。 在 IB 中打开 Xib 文件,添加一个 UITableViewCell
对象,将其标识符设置为“MyCellIdentifier”,并将其类设置为 MyCell 并添加组件。 最后,将 IBOutlets
连接到组件。 请注意,我们没有在 IB 中设置文件所有者。
其他方法提倡设置文件所有者,并在未通过附加工厂类加载 Xib 时警告内存泄漏。 我在 Instruments/Leaks 下测试了上述内容,没有发现内存泄漏。
那么从 Xibs 加载传感器的规范方法是什么? 我们是否设置文件的所有者? 我们需要工厂吗? 如果是这样,工厂的代码是什么样的? 如果有多种解决方案,让我们阐明每种解决方案的优缺点......
The question is simple: How do you load custom UITableViewCell
from Xib files? Doing so allows you to use Interface Builder to design your cells. The answer apparently is not simple due to memory managment issues. This thread mentions the issue and suggests a solution, but is pre NDA-release and lacks code. Here's a long thread that discusses the issue without providing a definitive answer.
Here's some code I've used:
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];
}
To use this code, create MyCell.m/.h, a new subclass of UITableViewCell
and add IBOutlets
for the components you want. Then create a new "Empty XIB" file. Open the Xib file in IB, add a UITableViewCell
object, set its identifier to "MyCellIdentifier", and set its class to MyCell and add your components. Finally, connect the IBOutlets
to the components. Note that we did not set the File's Owner in IB.
Other methods advocate setting the File's Owner and warn of memory leaks if the Xib is not loaded via an additional factory class. I tested the above under Instruments/Leaks and saw no memory leaks.
So what's the canonical way to load cells from Xibs? Do we set File's Owner? Do we need a factory? If so, what's the code for the factory look like? If there are multiple solutions, let's clarify the pros and cons of each of them...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
正确的解决方案是这样的:
The right solution is this:
以下是 的两种方法原作者表示是由 IB 工程师推荐的。
有关更多详细信息,请参阅实际帖子。 我更喜欢方法#2,因为它看起来更简单。
方法#1:
方法#2:
更新(2014):
方法 #2 仍然有效,但不再有相关文档。 它曾经位于官方文档中,但现在已在故事板的青睐。
我在 Github 上发布了一个工作示例:
https://github.com/bentford/NibTableCellExample
针对 Swift 4.2 进行编辑
Here are two methods which the original author states was recommended by an IB engineer.
See the actual post for more details. I prefer method #2 as it seems simpler.
Method #1:
Method #2:
Update (2014):
Method #2 is still valid but there is no documentation for it anymore. It used to be in the official docs but is now removed in favor of storyboards.
I posted a working example on Github:
https://github.com/bentford/NibTableCellExample
edit for Swift 4.2
注册
在 iOS 7 之后,此过程已简化为(swift 3.0):
出队
然后,使用 (swift 3.0) 出队:
不同之处在于,这种新方法不仅使单元出队,还会创建不存在的单元格(这意味着您不必执行
if (cell == nil)
恶作剧),并且单元格就可以使用了,就像上面的例子一样。当然,单元格关联类的类型是您在 .xib 文件中为
UITableViewCell
子类定义的类型,或者使用其他注册方法。配置
理想情况下,您的单元格在注册时就已经在外观和内容定位(如标签和图像视图)方面进行了配置,并且在
cellForRowAtIndexPath
方法中您只需填写它们即可。
当然,这些都可以在 ObjC 中以相同的名称使用。
Register
After iOS 7, this process has been simplified down to (swift 3.0):
Dequeue
And later on, dequeued using (swift 3.0):
The difference being that this new method not only dequeues the cell, it also creates if non-existant (that means that you don't have to do
if (cell == nil)
shenanigans), and the cell is ready to use just as in the example above.And of course, the type of the associated class of the cell is the one you defined in the .xib file for the
UITableViewCell
subclass, or alternatively, using the other register method.Configuration
Ideally, your cells have been already configured in terms of appearance and content positioning (like labels and image views) by the time you registered them, and on the
cellForRowAtIndexPath
method you simply fill them in.All together
And of course, this is all available in ObjC with the same names.
接受了肖恩·克拉弗的回答并对其进行了一些清理。
BBCell.h:
BBCell.m:
我创建了 BBCell 的所有 UITableViewCell 子类,然后将标准替换
为:
Took Shawn Craver's answer and cleaned it up a bit.
BBCell.h:
BBCell.m:
I make all my UITableViewCell's subclasses of BBCell, and then replace the standard
with:
我使用了 Bentford 的方法#2:
它有效,但要注意与自定义 UITableViewCell .xib 文件中的文件所有者的连接。
通过传递 <在
loadNibNamed
语句中的 code>owner:self 中,将UITableViewController
设置为UITableViewCell
的文件所有者。如果您拖放到 IB 中的头文件来设置操作和出口,默认情况下它会将它们设置为文件所有者。
在
loadNibNamed:owner:options
中,Apple 的代码将尝试在您的UITableViewController
上设置属性,因为它是所有者。 但是你没有在那里定义这些属性,所以你会得到一个关于键值编码兼容的错误:如果一个事件被触发,你会得到一个 NSInvalidArgumentException:
一个简单的解决方法是将 Interface Builder 连接指向
UITableViewCell
而不是 File's Owner:I used bentford's Method #2:
It works, but watch out for connections to File's Owner in your custom UITableViewCell .xib file.
By passing
owner:self
in yourloadNibNamed
statement, you set theUITableViewController
as File's Owner of yourUITableViewCell
.If you drag and drop to the header file in IB to set up actions and outlets, it will set them up as File's Owner by default.
In
loadNibNamed:owner:options
, Apple's code will try to set properties on yourUITableViewController
, since that's the owner. But you don't have those properties defined there, so you get an error about being key value coding-compliant:If an Event gets triggered instead, you'll get an NSInvalidArgumentException:
An easy workaround is to point your Interface Builder connections at the
UITableViewCell
instead of File's Owner:我决定发帖,因为我不喜欢这些答案中的任何一个——事情总是可以更简单,这是迄今为止我发现的最简洁的方法。
1. 根据需要在 Interface Builder 中构建您的 Xib
2。 在你的 UIViewController 或 UITableViewController 子类中
3. 在您的 MyTableViewCellSubclass 中
I've decided to post since I don't like any of these answers -- things can always be more simple and this is by far the most concise way I've found.
1. Build your Xib in Interface Builder as you like it
2. In your UIViewController or UITableViewController subclass
3. In your MyTableViewCellSubclass
如果您使用 Interface Builder 来创建单元格,请检查您是否已在检查器中设置了标识符。 然后检查调用dequeueReusableCellWithIdentifier时是否相同。
我不小心忘记在一个表重的项目中设置一些标识符,性能变化就像白天和黑夜。
If you're using Interface Builder to make cells, check that you've set the Identifier in the Inspector. Then check that it's the same when calling dequeueReusableCellWithIdentifier.
I accidentally forgot to set some identifiers in a table-heavy project, and the performance change was like night and day.
从 XIB 加载 UITableViewCells 可以节省大量代码,但通常会导致滚动速度很糟糕(实际上,不是 XIB 而是过度使用 UIView 导致了这种情况)。
我建议您看一下: 链接参考
Loading UITableViewCells from XIBs saves a lot of code, but usually results in horrible scrolling speed (actually, it's not the XIB but the excessive use of UIViews that cause this).
I suggest you take a look at this: Link reference
以下是我一直用于从 XIB 创建自定义单元的类方法:
然后,在 XIB 中,我设置类名称并重用标识符。 之后,我可以在我的视图控制器中调用该方法,而不是
它足够快,并且在我的两个运输应用程序中使用。 它比调用 [nib objectAtIndex:0] 更可靠,至少在我看来,比 Stephan Burlot 的示例更可靠,因为您保证只从 XIB 中获取正确的视图类型。
Here's the class method that I've been using for creating custom cells out of XIBs:
Then, in the XIB, I set the class name, and reuse identifier. After that, I can just call that method in my view controller instead of the
It's plenty fast enough, and being used in two of my shipping applications. It's more reliable than calling
[nib objectAtIndex:0]
, and in my mind at least, more reliable than Stephan Burlot's example because you're guaranteed to only grab a view out of a XIB that is the right type.正确的解决方案是这样的
Correct Solution is this
重新加载 NIB 的成本很高。 最好加载一次,然后在需要单元格时实例化对象。 请注意,您可以使用此方法将 UIImageViews 等添加到笔尖,甚至是多个单元格(Apple 的“registerNIB”iOS5 只允许一个顶级对象 - Bug 10580062
“iOS5 tableView registerNib:过度限制”
所以我的代码如下 - 你在 NIB 中读取一次(像我一样初始化或在 viewDidload 中 - 无论如何。从那时起,你将 nib 实例化为对象,然后选择你需要的一个。这比反复加载笔尖要高效得多。
Reloading the NIB is expensive. Better to load it once, then instantiate the objects when you need a cell. Note that you can add UIImageViews etc to the nib, even multiple cells, using this method (Apple's "registerNIB" iOS5 allows only one top level object - Bug 10580062
"iOS5 tableView registerNib: overly restrictive"
So my code is below - you read in the NIB once (in initialize like I did or in viewDidload - whatever. From then on, you instantiate the nib into objects then pick the one you need. This is much more efficient than loading the nib over and over.
检查这个 - http://eppz.eu/blog/custom-uitableview-cell/ - 使用一个小类的非常方便的方法,该类在控制器实现中以一行结束:
Check this - http://eppz.eu/blog/custom-uitableview-cell/ - really convenient way using a tiny class that ends up one line in controller implementation:
正确的方法是创建 UITableViewCell 子类实现、标头和 XIB。 在 XIB 中删除所有视图并仅添加一个表格单元格。 将类设置为 UITableViewCell 子类的名称。 对于文件所有者,将其设为 UITableViewController 子类类名。 使用 tableViewCell 出口将文件所有者连接到单元格。
在头文件中:
在实现文件中:
The correct way to do it is to create a UITableViewCell subclass implementation, header, and XIB. In the XIB remove any views and just add a table cell. Set the class as the name of the UITableViewCell subclass. For file owner, make it the UITableViewController subclass class name. Connect the file owner to the cell using the tableViewCell outlet.
In the header file:
In the implementation file:
我要做的就是在控制器类中声明一个
IBOutlet UITableViewCell *cell
。然后调用 NSBundle loadNibNamed 类方法,该方法会将 UITableViewCell 提供给上面声明的单元格。
对于 xib,我将创建一个空的 xib 并在 IB 中添加 UITableViewCell 对象,可以根据需要对其进行设置。 然后,该视图连接到控制器类中的单元
IBOutlet
。NSBundle 添加 loadNibNamed(ADC 登录)
cocoawithlove.com 文章 I该概念源自(获取电话号码示例应用程序)
What I do for this is declare an
IBOutlet UITableViewCell *cell
in your controller class.Then invoke the
NSBundle loadNibNamed
class method, which will feed theUITableViewCell
to the cell declared above.For the xib I will create an empty xib and add the
UITableViewCell
object in IB where it can be setup as needed. This view is then connected to the cellIBOutlet
in the controller class.NSBundle additions loadNibNamed (ADC login)
cocoawithlove.com article I sourced the concept from (get the phone numbers sample app)
从
UITableViewCell
创建您自己的自定义类AbcViewCell
子类(确保您的类文件名和 nib 文件名相同)创建此扩展类方法。
使用它。
let cell: AbcViewCell = UITableViewCell.fromNib()
Create your own customized class
AbcViewCell
subclass fromUITableViewCell
(Make sure your class file name and nib file name are the same)Create this extension class method.
Use it.
let cell: AbcViewCell = UITableViewCell.fromNib()
首先导入自定义单元格文件
#import "CustomCell.h"
,然后更改委托方法,如下所述:First import your custom cell file
#import "CustomCell.h"
and then change the delegate method as below mentioned:在 Swift 4.2 和 Xcode 10 中,
我在 ViewDidLoad 中有三个 XIB 单元文件
像这样注册您的 XIB 文件...
这是第一种方法
第二种方法直接在 cellForRowAt indexPath: 中注册 XIB 文件>
这是我的表视图委托函数
In Swift 4.2 and Xcode 10
I have three XIB cell files
in ViewDidLoad register your XIB files like this...
This is first approach
Second approach directly register XIB files in cellForRowAt indexPath:
This is my tableview delegate functions
这是我的方法: 从 XIB 文件加载自定义 UITableViewCells...另一种方法
这个想法是使用
IBOutlet UIView *content
创建UITableViewCell
的 SampleCell 子类属性以及您需要从代码配置的每个自定义子视图的属性。 然后创建一个SampleCell.xib 文件。 在此 nib 文件中,将文件所有者更改为 SampleCell。 添加大小适合您的需求的内容UIView
。 添加并配置您想要的所有子视图(标签、图像视图、按钮等)。 最后,将内容视图和子视图链接到文件所有者。Here is my method for that: Loading Custom UITableViewCells from XIB Files… Yet Another Method
The idea is to create a SampleCell subclass of the
UITableViewCell
with aIBOutlet UIView *content
property and a property for each custom subview you need to configure from the code. Then to create a SampleCell.xib file. In this nib file, change the file owner to SampleCell. Add a contentUIView
sized to fit your needs. Add and configure all the subviews (label, image views, buttons, etc) you want. Finally, link the content view and the subviews to the file owner.以下是在
UITableView
中注册单元格的通用方法:说明:
Reusable
协议根据其类名生成单元格 ID。 确保遵循约定:单元 ID == 类名称 == 笔尖名称
。UITableViewCell
符合Reusable
协议。UITableView
扩展抽象了通过 nib 或类注册单元格的差异。使用示例:
Here is a universal approach for registering cells in
UITableView
:Explanation:
Reusable
protocol generates cell ID from its class name. Make sure you follow the convention:cell ID == class name == nib name
.UITableViewCell
conforms toReusable
protocol.UITableView
extension abstracts away the difference in registering cells via nib or class.Usage example:
我不知道是否有规范的方法,但这是我的方法:
并使用以下代码:
在您的示例中,
如果 Apple 更改 xib 中项目的顺序,则使用可能会中断。
I dont know if there is a canonical way, but here's my method:
And use this code:
In your example, using
may break if Apple changes the order of items in the xib.
此扩展需要 Xcode7 beta6
创建一个仅包含 1 个自定义 UITableViewCell 的 Xib 文件。
加载它。
This extension requires Xcode7 beta6
Create an Xib file that contains just 1 custom UITableViewCell.
Load it.