我会直接跳到它。
我有一个带有titleLabel的单元格,用于打印代码和一个detailTextLabel,用于打印该代码的解码版本。我有一个plist字典数据库,其中包含数百个这样的字典:“JD”:“John Doe”。
在cellForRow中设置detailTextLabel时,我最初有一个函数查找plist中的代码并返回解码后的值。这很有效,但卷轴非常缓慢。
我现在正在做同样的事情,但这次解码查找在后台发生并在回调闭包中返回结果。滚动很流畅,工作正常。几乎。有时,为错误的单元格返回解码的值(因为它被重用)。这是可以预防的吗?
以下是我在cellForRow函数中的内容:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Create the cell
let cell = tableView.dequeueReusableCellWithIdentifier("RosterCell", forIndexPath: indexPath) as UITableViewCell
if let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as? Object
{
cell.textLabel?.text = object.rawCode
CodeDecoder.getDescriptionInBackground(CodeType.All, code: rawCode) { (description) -> () in
if description.isEmpty == false {
cell.detailTextLabel?.text = description
}
else {
cell.detailTextLabel?.text = "No decode found for code: \(object.rawCode)."
}
}
}
else {
cell.textLabel?.text = "Object not found."
cell.detailTextLabel?.text = ""
}
return cell
}
我非常感谢有关此方面的任何帮助或想法。
答案 0 :(得分:2)
这里的其他答案都是很好的建议,但没有找到你所拥有的根本问题。由于您已经确定需要异步解码数据,现在需要弄清楚在重用该单元时如何取消该操作。我冒昧地猜测以下方法:
CodeDecoder.getDescriptionInBackground(CodeType.All, code: rawCode)
在某个后台队列中以dispatch_async
运行。
相反,您可以使用NSOperationQueue
生成可以取消的NSOperation
个对象。然后在prepareForReuse
方法中,您可以取消可能正在运行的操作。以下是一个如何运作的简单示例。
<强> CodeDecoder 强>
class CodeDecoder {
let operationQueue: NSOperationQueue
enum CodeType {
case All
}
init() {
self.operationQueue = NSOperationQueue()
self.operationQueue.qualityOfService = .UserInitiated
self.operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount
}
func getDescriptionInBackground(#codeType: CodeType, code: Int, completionHandler: (String) -> Void) -> NSOperation {
let operation = NSBlockOperation()
weak var weakOperation = operation
operation.addExecutionBlock {
if let strongOperation = weakOperation {
if strongOperation.cancelled {
return
}
// Run your decode logic
if strongOperation.cancelled {
return
}
dispatch_async(dispatch_get_main_queue()) {
completionHandler("decoded value")
}
}
}
self.operationQueue.addOperation(operation)
return operation
}
}
<强> CustomViewController 强>
class CustomViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Create the cell
let cell = tableView.dequeueReusableCellWithIdentifier("RosterCell", forIndexPath: indexPath) as CustomTableViewCell
if let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as? Object {
cell.textLabel?.text = object.rawCode
let operation = CodeDecoder.getDescriptionInBackground(CodeType.All, code: rawCode) { description in
if description.isEmpty == false {
cell.detailTextLabel?.text = description
}
else {
cell.detailTextLabel?.text = "No decode found for code: \(object.rawCode)."
}
}
cell.operation = operation
}
else {
cell.textLabel?.text = "Object not found."
cell.detailTextLabel?.text = ""
}
return cell
}
}
<强> CustomTableViewCell 强>
class CustomTableViewCell: UITableViewCell {
var operation: NSOperation?
override func prepareForReuse() {
super.prepareForReuse()
if let operation = operation {
if !operation.finished {
operation.cancel()
self.operation = nil
}
}
}
}
绝对要仔细检查弱化/强化逻辑。我无法测试它,因为这不是一个完全编译的代码块。这应该允许您在后台运行所有解码,更新主线程上的单元格,并在需要重用单元格时取消解码操作。
答案 1 :(得分:1)
是的,这是可以预防的。
你的问题来自CollectionViewCells
被重用的事实,就像你说的那样,如果你在主线程中加载文本,它需要一段时间,直到一个单元被加载并且可以被呈现(因为文本查找)。
你可以做的是一些事情。
1)将您想要在早期阶段呈现的文本加载到数组中(先前viewController
,加载屏幕),然后从该数组加载collectionViewCells
。这将导致装载量大幅下降。
1.1)如果你不想在加载单元格之前加载所有数据,你可以编写一种机制,一旦加载了x个单元格(或类似的东西)就在后台加载更多数据
2)您可以在之前的时间创建UICollectionViewCells
池。将从该池加载单元格,并将每个新分配的单元格插入该池中。
您可以执行其他几种机制,但这些是基础机制
答案 2 :(得分:0)
为什么不用对象属性创建UITableViewCell(例如ObjectTableViewCell)的子类?然后,在tableView:rowAtIndexPath中:您的代码将是这样的:
let cell = tableView.dequeueReusableCellWithIdentifier("RosterCell", forIndexPath: indexPath) as ObjectTableViewCell
cell.object = self.fetchedResultsController.objectAtIndexPath(indexPath) as? Object
return cell
在ObjectTableViewCell类中:
var object: Object? {
didSet {
//check object for nil and do other your code here
}
}