UICollectionView滚动毛刺/滞后

时间:2018-04-23 10:49:12

标签: 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"
}

3 个答案:

答案 0 :(得分:1)

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

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

  

UIImage在第一次实际使用/绘制图像之前不会读取和解码图像。要强制在后台线程上执行此工作,您必须在执行主线程-setImage:call之前在后台线程上使用/绘制图像。许多人认为这是违反直觉的。我在另一个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.

希望这会有所帮助:)

答案 1 :(得分:0)

您应该尝试应用缓存图片并相应地显示它们; 看看下面的类似问题和苹果文档 - :

How to use NSCache

A mutable collection you use to temporarily store transient key-value pairs that are subject to eviction when resources are low.

答案 2 :(得分:0)

啊我遇到了同样的问题,每次滚动时都会尝试重新计算layoutAttributes。要解决此问题,请尝试覆盖自定义单元格类preferredLayoutAttributesFitting,如下所示:

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        return layoutAttributes
    }