在可重复使用的单元格的背景中加载文本内容

时间:2015-02-17 12:09:33

标签: ios uitableview swift

我会直接跳到它。

我有一个带有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

}

我非常感谢有关此方面的任何帮助或想法。

3 个答案:

答案 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
   }
}