ios - UICollectionView 滚动故障/滞后

标签 ios swift uicollectionview uicollectionviewcell

在我的 iOS 项目中,我使用带有自定义单元格的常规 UICollectionView

该单元格接收一些属性,当其中一个对象被设置时,我对该单元格执行一些 UI 更新。

虽然当我滚动时我感觉到滚动有轻微的故障,但我不知道如何改进我的代码以使其运行更流畅。

我运行了 Intruments 并运行了 Time Profiler,当出现这些滞后时,主线程 CPU 使用率达到 100%,如您在此图中所见:

main thread spikes

跟踪它在工具中的使用百分比:

trace 1

进一步追踪到 png_read_IDAT_data

trace 2

现在看代码:

在我的 UICollectionViewController cellForItem(:_)

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: homeCellId, for: indexPath) as! HomePostCollectionViewCell
    cell.post = self.posts[indexPath.item]
    cell.delegate = self
    cell.layer.cornerRadius = 5
    cell.layer.borderWidth = 0.7
    cell.layer.borderColor = UIColor.rgb(red: 204, green: 204, blue: 204).cgColor
    cell.layer.masksToBounds = true
    return cell     
}

在我的 UICollectionViewCell 中,每当设置 post 变量时,我都会执行以下 UI 更新:

public var post: Post? {
    didSet {
        guard let post = self.post else { return }
        userProfileImageView.image = post.user.profileImage
        nameLabel.text = post.user.name
        titleTextView.text = post.title
        creationDateLabel.text = post.creationDate.timeAgoDisplay()

        thumbnailImageView.image = post.thumbnail
        if backgroundImageOffset != nil {
            setBackgroundImageOffset(imageOffset: backgroundImageOffset)
        } else {
            setBackgroundImageOffset(imageOffset: CGPoint.zero)
        }
    }
}

你能帮我追踪这个问题以及如何改进它吗?

谢谢。

编辑

我已经注释掉了 cell.post= 行,这样做主线程 CPU 使用率下降到大约 10%,因此可以确保问题出在 post { didSet{} } 构造函数。

你能看到有什么有用的改进吗?

编辑2

这里是timeAgoDisplay()函数的代码

func timeAgoDisplay() -> String {
    let secondsAgo = Int(Date().timeIntervalSince(self))

    let minute = 60
    let hour = 60 * minute
    let day = 24 * hour
    let week = 7 * day
    let month = 4 * week

    let quotient: Int
    let unit: String
    if secondsAgo < minute {
        quotient = secondsAgo
        unit = "second"
    } else if secondsAgo < hour {
        quotient = secondsAgo / minute
        unit = "min"
    } else if secondsAgo < day {
        quotient = secondsAgo / hour
        unit = "hour"
    } else if secondsAgo < week {
        quotient = secondsAgo / day
        unit = "day"
    } else if secondsAgo < month {
        quotient = secondsAgo / week
        unit = "week"
    } else {
        quotient = secondsAgo / month
        unit = "month"
    }

    return "\(quotient) \(unit)\(quotient == 1 ? "" : "s") ago"
}

最佳答案

我也遇到过像你这样的问题,认为 UIImage(data:) 可以在后台线程上使用,然后将结果图像应用到 UIImageViewmain 队列上。

然而事实并非如此,引用了关于这个主题的一个很好的答案 UIImage initWithData: blocking UI thread from async dispatch? :

UIImage doesn't read and decode the image until the first time it's actually used/drawn. To force this work to happen on a background thread, you have to use/draw the image on the background thread before doing the main thread -setImage: call. Many folks find this counter-intuitive. I explained this in considerable detail in another answer.

如上所示,链接中的答案表明您必须在使用图像之前在后台线程上use/draw图像。

这是我发现如何在后台线程上强制解压的示例:

Preload UIImage for super-smooth interaction. especially great if you use JPGs, which otherwise produce a noticeable lag on the main thread.

希望这有帮助:)

关于ios - UICollectionView 滚动故障/滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49979263/

相关文章:

ios - 具有不同单元格大小的 UICollectionView

ios - CollectionView Label 多行标签自动布局在小屏幕 iOS 设备上陷入困境

ios - 如何在每个切片中制作不同数量的细胞

ios - Swift 中是否有默认的加密功能来加密/解密字符串?

ios - 背景图像居中和自动布局惨败

swift - 如何在 Swift 中找到 UTF8String 的长度?

swift - 在上传到 Google Cloud 存储之前调整图像大小并压缩图像

swift - 运行 HKStatisticsCollectionQuery 示例代码时出现 "ambiguous use of ' 组件错误

swift - 将单个 UICollectionViewCell 对齐到 collectionView 的左侧

ios - 如何阻止应用程序作为服务在 iOS 上运行