从 Storyboard 加载 tableViewCell 而不使用 dequeueReusableCellWithIdentifier

Posted

技术标签:

【中文标题】从 Storyboard 加载 tableViewCell 而不使用 dequeueReusableCellWithIdentifier【英文标题】:Load tableViewCell from Storyboard without using dequeueReusableCellWithIdentifier 【发布时间】:2013-12-03 21:14:57 【问题描述】:

我想从情节提要加载一个 tableViewCell,而不使用 dequeueReusableCellWithIdentifier 创建一个原型单元格以在调用 cellForRowAtIndex 之前引用。在 CellForRowAtIndex 之外调用它会做一些有趣的事情。 dequeueReusableCellWithIdentifier 仍然像往常一样在 cellForRowAtIndex 中使用。

我根据其内容动态设置单元格高度。这需要我知道 tableViewCell 中视图的大小、位置、文本属性(如字体大小、对齐方式等)在哪里。否则我必须对这些值进行硬编码以匹配情节提要中的内容。

我目前正在做的是创建一个仅包含单元格的新 xib 文件,从 viewDidLoad 加载它,并保留指向它的指针。

-(void)viewDidLoad 
    // typical coding stuff goes here 

    // load nib
    UINib *nib = [UINib nibWithNibName:@"ContentTextCell" bundle:nil];

    // assign nib to identifier
    [self.tableView registerNib:nib forCellReuseIdentifier:@"ContentTextCell"];

    // reference cell
    NSArray *topLevelObjects = [nib instantiateWithOwner:nil options:nil];
    _referenceContentTextCell = [topLevelObjects objectAtIndex:0];

有什么方法可以让我加载表格视图单元而不使其成为自己的笔尖?使用 dequeueReusableCellWithIdentifier:forIndexPath: 会导致 tableView 行为不正确。

附加说明 我想很多人的印象是我将代码称为 cellForRowAtIndex。它在 viewDidLoad 中调用。它总是这样显示,但在略读问题时可能很容易理解。我还在 cellForRowAtIndex 中照常使用 dequeueResuableCell。只是想说明清楚。

行高是动态的。如果您提出我应该将行高设为 44 的建议,那么您可能需要在尝试回答问题之前更仔细地阅读该问题。

我使用的文本来自 json 文件,它需要 textView 的段落、字体和定位来计算文本高度,这会影响行高。我想从原型单元中提取这些数据,而不是对值进行硬编码,并确保它们与情节提要中的内容相匹配。

代码已按原样运行。它运行良好。我只是认为能够从情节提要中提取原型单元而不是为其创建新的 xib 会更方便。

【问题讨论】:

您不想使用这种方法。我试过了。当你最终让它工作时,它会表现不佳并且会阻塞你的主线程。当你把它放在后台线程上时,它不会工作,因为 UIKit 不喜欢在后台线程上。它真的很聪明,可惜它不起作用,但你最好将字符串大小和填充相加。 只是为了让我们在同一个页面上,你是说加载一个笔尖,特别是一个带有一个 tableViewCell 的笔尖会阻塞主线程?它是一个小笔尖,如果你在 textedit 中打开它它不到一页。你认为这段代码在 cellForRowAtIndex 中吗?那将是可怕的,但它只在 viewDidLoad 中。我认为它不会在任何可测量的时间内阻塞主线程。 我并不是说加载笔尖会有问题。我是说:如果您有一个包含大量数据的数组,并且您遍历数组中的每个项目,并将每个项目输入到您的单元格中,然后对项目进行布局以计算高度......这可能会阻塞主要线程明显。 我明白了,这会在很大程度上影响性能。但我不遍历数组。我直接用 indexPath 索引数据,O(1) 时间,让文本值计算 tableView:heightForRowAtIndexPath: 上的高度。感谢您的意见。 您不能从情节提要中拉出 UIView,但可以拉出 UIViewController。如果您更愿意使用故事板,您可以制作一个仅将单元格作为子视图的虚拟控制器,但这可能与您当前的方法一样奇怪。 【参考方案1】:

如果我理解正确,行高是您的模型和单元格中子视图的某些属性的函数,例如,模型中的字符串和单元格文本视图中的字体大小。

我同意将单元格子视图的视图属性保留在代码中似乎与将它们保留在情节提要中是多余的,但您也说得对,保留参考单元格很奇怪。奇怪的是您是从笔尖获取单元格还是找到从情节提要中获取单元格的方法(我认为您做不到,这是您所陈述问题的答案)。

在 heightForRowAtIndexPath 中将单元格出列没有多大意义,并且可能会导致无限递归循环,我相信您已经找到了。

所以你可能不会喜欢这个答案,但我认为最好的主意是“改变态度的冗余”。 “冗余”意味着您将视图属性保留在代码中,就像返回 textView 所需字体 pointSize 的方法一样。 “态度改变”,意味着你不认为这是多余的。相反,您的代码应该是与行高相关的所有视图属性的权威。将故事板中的原型单元视为可视化正确编码值的一种方式。我什至建议在配置单元格时使用编码值在代码中设置属性,尤其是在它们数量不多的情况下。

最后,如果你的 rowHeight 计算很精细,而且听起来确实如此,请务必也实现

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

并在其中进行更快的计算(例如返回一个常数)。

【讨论】:

您关于从 tableview 调用 cellAtIndexPath 会导致一些无限递归的断言是正确的。你是唯一一个明白我的问题的人。调用 dequeue 的行为很奇怪。我将传入索引第 0 行第 0 行,第一行将被弄乱。可能是我不够清晰。如果你们愿意,就怪我。我理解你所说的“以改变态度的冗余”的意思,代码是大多数事情的权威,除了定位/大小和文本属性,我认为界面构建器在做这件事上更快更有效。 总之,在没有 dequeueForRowAtIndexPath 的情况下加载原型单元格的答案是你不能。这不是给你的,而是给任何想知道我一样的人的人。谢谢你。【参考方案2】:

我刚刚解决了一个几乎相同的问题。您想要高度,但高度取决于一堆单元格 UI 设置。检查图像上方和下方的填充高度甚至可能是一项相当简单的任务。在完成之前可能会在 IB 中进行多次调整的东西,因此您不想在代码中复制填充值,您想根据 IB 中的当前值检查它们。

您可以从任何地方调用dequeueReusableCellWithIdentifier:(假设您有对UITableView 的引用)。所以你不需要其他方法来获取细胞。这不会导致递归或无限循环问题。

但有一个问题。该单元不会返回到自动重用池。 UITableView 计算出哪些单元格从屏幕上滚动出来并将它们放入池中,但是这个单元格永远不会出现在屏幕上,因此表格视图永远不会将其从屏幕上移除并放回池中。更糟糕的是,该表仍然对它有很强的引用;所以如果你丢失了你的参考,它不会被清理,它本质上是内存泄漏。你不能使用它,表格视图也不能使用它,它会一直保留在内存中,直到支持 UITableView 被释放。

我找到了一些解决问题的方法:

    将单元格出列一次,将其存储在安全的地方,并在每次需要测试某些单元格格式时使用相同的单元格。您甚至可以在每次想要测试新内容时致电prepareForReuse: 进行清理。在内存中保留一个额外的单元格不太可能成为大多数应用的杀手锏。 将其偷偷放回重用池中。等等,什么?是的,我说你不能这样做,但你可以,但这个想法让我有点畏缩。下次您到达 cellForRowAtIndexPath: 时,不要将新单元格出列,使用您已经放置的单元格并返回该单元格。这会将您任性的单元格滑回折叠处,并最终使其回到重用池中。

所以你可以这样做....应该吗?嗯,这是一个不同的问题。 Apple 提供了一些替代方案来尝试解决此问题,但在某些情况下它们无法有效工作。

【讨论】:

Apple 提供了哪些替代方案?请问可以提供一些链接吗?【参考方案3】:

这是一个糟糕的模式,你会开始出现一些你已经看到的不受欢迎的行为。将height 作为类方法存储在单元格中可能会更好。

+ (CGFloat)requiredHeight;
 
  return 44.f;

这样称呼它

self.tableView.rowHeight = [OKACell requiredHeight];

这确实意味着您需要在两个地方管理高度,但由于描述性名称和类方法,到时候更改应该不难。

【讨论】:

你试过那个 cell = [tableView dequeue...?很确定 heightForRowAtIndexPath 中的出队会导致无限递归循环。 奥利弗,我想你误解了我的问题。我在我的问题中写了额外的注释。虽然 Danh 有最正确的答案。哪个不是。您无法从情节提要中加载原型单元。 @Biclops,如果你的行高是动态的,你应该使用你的模型来决定单元格的高度,而不是视图。模型-视图-控制器分离是一个很好的模式。您可以使用sizeWithFont:forWidth:lineBreakMode: 在 tableView heightForRow 委托方法中计算单元格的高度。 我可以,但我从哪里获得字体、宽度和 lineBreakMode?我应该从我将要使用的标签/文本视图中获取它们,否则我将不得不对这些值进行硬编码。

以上是关于从 Storyboard 加载 tableViewCell 而不使用 dequeueReusableCellWithIdentifier的主要内容,如果未能解决你的问题,请参考以下文章

TableView 数据覆盖导航栏

Swift:使用 StoryBoard 在 Tableview 上浮动加号按钮

在 iOS 中使用 Storyboard 将图像设置为 TableView 的背景

从 Storyboard 加载 tableViewCell 而不使用 dequeueReusableCellWithIdentifier

如何将视图带到 StoryBoard 中的 tableview 内容视图的顶部?

从 AppDelegate 加载 Storyboard 会在窗口上留下黑色空间