我有一个UITableViewCell,里面有一个水平的UIStackView。创建UIViews并将其添加到堆栈视图会导致一些性能问题。表格视图的滚动中有一个确定的斑点。我已经在仪器中对其进行了分析,并且调用更新堆栈视图是整个应用程序中第二大的任务。

设置如下。我正在使用MVP,并且在演示者中使用该单元格,在设置单元格时调用updateStackView(with items: [Any]),并在cellForRowAtIndexPath方法中设置了该单元格。

我的原始updateStackView(with items: [Any])如下:

func updateStackView(_ items: [Any]) {
    items.forEach { item in
        if let customViewModel = item as? CustomViewModel {
            myStackView.addArrangedSubview(CustomView(customViewModel))
        } else if item is Spacing {
            myStackView.addArrangedSubview(PipeSpacerView())
        }
    }
}

所以这是瓶颈,我决定将一些内容移到后台,如下所示:
func updateStackView(_ items: [Any]) {
    DispatchQueue.global(qos: .background).async {
        items.forEach { item in
            if let customViewModel = item as? CustomViewModel {
                DispatchQueue.main.async {
                    self.myStackView.addArrangedSubview(CustomView(customViewModel))
                }
            } else if item is Spacing {
                DispatchQueue.main.async {
                    self.myStackView.addArrangedSubview(PipeSpacerView())
                }
            }
        }
    }
}

Kinda丑陋,因为它具有一些深层嵌套,但对性能产生了很大影响。

我对此感到满意,直到我向下滚动到表格视图的底部并开始向上滚动。我注意到当单元格滚动到视图中时,某些CustomViews在堆栈视图中出现了两次。单元格完全可见后,堆栈视图在大多数情况下会自动更正为正确的数字。

在我的prepareForReuse中,我确实对以下函数进行了调用:
func resetDescriptionStackView() {
    descriptionStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
}

一直都在工作,直到一切都在主线程上。

任何帮助,不胜感激。我可以肯定这是一个线程问题,因为当我将内容移至后台时才开始抬起头。

最佳答案

首先,.background服务质量主要用于大型/艰苦的后台工作(例如,发送统计数据,与服务器同步数据库等),但是当您想在不同于main的队列上进行一些快速工作然后更新时UI正确使用.userInitiated或.utility的方式。另外,我建议您将自己的OperationQueue与后台Qos之一一起使用。
为了使代码更好,您可以使用“defer”语句(documentation)并将代码分隔为小函数。

如果要一次添加子视图,则可以为CustomView和Spacing添加特殊ID,然后进行检查。
完整代码:

//For each our CustomView and Spacing we set uniq id to tag property in init of each class.

        /// Operation queue that work with our UIStackView
        fileprivate lazy var stackViewOperationQueue: OperationQueue = {

            let queue = OperationQueue()
            queue.name = "StackView OperationQueue"
            queue.qualityOfService = .userInitiated
            return queue
        }()

        func updateStackView(_ items: [Any]) {

            stackViewOperationQueue.addOperation { [weak self] in
                items.forEach { self?.addArrangedSubviewToStackView(by: $0) }
            }
        }

       fileprivate func addArrangedSubviewToStackView(by item: Any) {

            var handler: () -> () = {}

            defer {
                DispatchQueue.main.async {
                    handler()
                }
            }

            if let customViewModel = item as? CustomViewModel {
                handler = { [weak self] in
                  let viewToAdd = CustomView(customViewModel)
                  self?.checkAndAddSubviewIfNeeded(viewToAdd)}
            }
            else if item is Spacing {
                handler = { [weak self] in
                  self?.checkAndAddSubviewIfNeeded(PipeSpacerView()) }
            }
        }

       func checkAndAddSubviewIfNeeded(_ subviewToAdd: UIView) {

           if !myStackView.arrangedSubviews.contains(where: { view in return view.tag == subviewToAdd.tag }) {
               self.myStackView.addArrangedSubview(subviewToAdd)
           }
       }

希望对您有所帮助!

关于ios - 从后台线程在UITableView中添加到UIStackView,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54574059/

10-15 14:09