目录

IOS UITableView性能优化

一、UITableView是什么?

UITableView是一个强大的视图组件,它可以显示一个滚动的列表数据。每个列表项被称为一个单元格(UITableViewCell)。UITableView的数据源(通常是一个数组)和委托(通常是视图控制器)提供了列表的内容和行为。

二、UITableView什么工作原理?

理解UITableView的工作原理对于优化其性能和用户体验至关重要。当你滚动表格时,UITableView并不会为所有单元格创建视图。相反,它使用了一种叫做"单元格重用"的技术,只创建屏幕上可见的单元格,并且当这些单元格滚动出屏幕时,会将其放入一个重用队列中。当新的单元格滚动到屏幕上时,UITableView会从重用队列中取出一个单元格,并对其进行配置。这就是为什么在cellForRowAt方法中,你总是需要配置单元格的所有属性------因为你无法确定这个单元格是新的,还是从重用队列中取出的。

三、 UITableView如何优化?

了解完上面的工作原理,我们就可以从以下几个方面进行优化

● 重用单元格

● 异步加载图片

● 预估单元格高度

● 避免离屏渲染

● 避免图片尺寸不一致

● 避免动态添加内容

● 滑动按需加载

1. 重用单元格

在UITableView中,单元格的重用是提高性能的关键。通过重用单元格,我们可以避免频繁地创建和销毁单元格对象,从而减少内存分配和释放的开销。

要实现单元格的重用,我们需要注册单元格的重用标识符,并在cellForRowAtIndexPath方法中使用dequeueReusableCellWithIdentifier方法获取可重用的单元格。下面是一个简单的示例:

swift 复制代码
// 注册单元格的重用标识符  
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")  
// 在cellForRowAtIndexPath方法中获取可重用的单元格  
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {  
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)  
    // 配置单元格的数据和界面  
    return cell  
}

2. 异步加载图片

在UITableView中加载图片时,如果图片较大或者网络加载较慢,可能会导致滚动卡顿。为了解决这个问题,我们可以使用异步加载图片的方式。

异步加载图片的核心思想是在后台线程中加载图片,然后在主线程中更新UI。这样可以避免阻塞主线程,提高滚动的流畅性。下面是一个使用SDWebImage库进行异步加载图片的示例:

swift 复制代码
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {  
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)  
    let imageUrl = "https://xxxxx/xxAA.jpg"  
    cell.imageView?.sd_setImage(with: URL(string: imageUrl), placeholderImage: UIImage(named: "placeholder"))  
    return cell  
}

3. 预估单元格高度

当UITableView包含可变高度的行时,如果逐行计算高度,会导致性能开销巨大,因为这种方法需要对每一行进行高度计算,从而增加了CPU和GPU的负担。因此为了优化性能,可以使用estimatedRowHeight属性来为单元格设置预估高度。

estimatedRowHeight属性允许您为UITableView设置一个估计的行高,以便在加载表格时预先分配足够的空间,而不需要逐行计算高度。通过使用estimatedRowHeight属性,您可以避免因逐行计算高度而产生的性能瓶颈,从而提高表格的滚动性能和响应速度。

需要注意的是,estimatedRowHeight只是一种估计值,实际行高可能会根据内容的不同而有所差异。因此,在使用estimatedRowHeight时,您需要根据表格中内容的特性和规律来选择一个合适的估计值,以确保表格的显示效果和性能达到一个良好的状态。

ini 复制代码
tableView.estimatedRowHeight = 10.0f;//预估行高度

4. 避免离屏渲染

使用圆角、阴影等视图功能的时候,会进行离屏渲染,而离屏渲染跟当前屏幕渲染又不同。它会单独开辟一块缓冲区进行渲染操作,这个过程相对较耗资源。

(1)对于圆角较好的做法是让美术提供圆角背景图,并给Cell设成背景图片。

(2)对于阴影效果可以通过以下代码设置

ini 复制代码
import UIKit  
  
extension UIImageView {  
    func addShadow() {  
        layer.masksToBounds = false  
        layer.shadowColor = UIColor.black.cgColor  
        layer.shadowOffset = CGSize(width: 0, height: 0)  
        layer.shadowOpacity = 0.5  
        layer.shadowRadius = 2  
        layer.shadowPath = UIBezierPath(rect: bounds).cgPath  
    }  
}

5.避免图片尺寸不一致

为了提高UITableView的性能,图片的尺寸应该与UIImageView的尺寸保持一致。通过将图片的尺寸调整为与UIImageView的尺寸相同,可以避免不必要的缩放和拉伸,从而减少计算资源和内存的占用。

此外,图片的contentMode也会对UITableView的滚动速度造成影响。如果图片的contentMode不正确,可能会导致图片加载速度变慢,从而影响应用程序的性能。因此,需要根据需要显示的图片大小来处理contentMode,以优化图片的显示效果。

5. 避免动态添加内容

在初始化UITableViewCell时,将所有需要展示的子视图添加到cell中,避免动态添加。这些子视图可以是自定义的UI元素,例如文本标签、图像视图、按钮等。

优化方法:添加子视图后,可以根据需要设置它们的隐藏属性,以控制它们的显示和隐藏。

通过在初始化时将所有子视图添加到cell中,可以确保它们在显示时已经存在,并且可以在需要时轻松地显示或隐藏它们。这种方法适用于在cell中展示动态内容的情况,例如根据数据源动态设置不同的视图内容。

需要注意的是,如果在初始化时添加了过多的子视图,可能会占用额外的内存和计算资源。因此,在添加子视图时应该考虑到这一点,并根据需要合理地管理内存和计算资源的使用。

6. 滑动按需加载

滑动按需载可分为两种:

● 分页加载

分页加载是指当用户滚动到UITableView底部时,自动加载下一页数据。这可以通过监听UITableView的滚动事件来实现。以下是一个示例代码:

swift 复制代码
func scrollViewDidScroll(_ scrollView: UIScrollView) {  
    let offsetY = scrollView.contentOffset.y  
    let contentHeight = scrollView.contentSize.height  
    let scrollViewHeight = scrollView.frame.height  
      
    if offsetY > contentHeight - scrollViewHeight {  
        // 加载下一页数据  
        loadNextPageData()  
    }  
}

● 滑动范围加载

滑动范围前后加载是指只在目标滚动范围的前后指定 3 行加载。这么处理是因为用户在快速滑动的过程中,大量快速滑动的内容是无用的。所以我们只需要加载最后滑动范围的内容即可。

less 复制代码
// 按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。  
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {  
    // 获取目标偏移量对应的索引路径  
    let ip = indexPathForRow(at: CGPoint(x: 0, y: targetContentOffset.pointee.y))  
    // 获取当前可见的第一个索引路径  
    if let cip = indexPathsForVisibleRows?.first {  
        let skipCount = 10 // 指定行数  
        // 如果目标行与当前行相差超过指定行数  
        if abs(cip.row - ip.row) > skipCount {  
            // 获取目标滚动范围内的所有索引路径  
            let temp = indexPathsForRows(in: CGRect(x: 0, y: targetContentOffset.pointee.y, width: self.width, height: self.height))  
            var arr = temp as NSArray as? [IndexPath] ?? []  
            // 如果滚动方向是向上  
            if velocity.y < 0 {  
                if let indexPath = temp.last, indexPath.row > 3 {  
                    // 在目标滚动范围前添加3个索引路径  
                    arr.append(IndexPath(row: indexPath.row-3, section: 0))  
                    arr.append(IndexPath(row: indexPath.row-2, section: 0))  
                    arr.append(IndexPath(row: indexPath.row-1, section: 0))  
                }  
            }  
            // 将需要加载的数据添加到数组中  
            self.needLoadDatas.append(contentsOf: arr)  
        }  
    }  
}

四、 总结

本文介绍 UITableView 性能优化的思路,在具体的业务应用中,可能会面临不同的场景和不同的需求,需要具体问题具体分析,但万变不离其中,如果大家有其它优化思路、疑问或者复杂场景,欢迎评论区一起交流~

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
Unlimitedz11 小时前
iOS内存管理中的强引用问题
macos·ios·cocoa
雨夜赶路人12 小时前
iOS开发--接入ADMob广告失败
ios
旭日猎鹰13 小时前
iOS崩溃堆栈分析
ios
SY.ZHOU14 小时前
Flutter 与原生通信
android·flutter·ios
鸿蒙布道师16 小时前
鸿蒙NEXT开发文件预览工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师16 小时前
鸿蒙NEXT开发全局上下文管理类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
归辞...17 小时前
【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(二)
笔记·ios·cocoa
码客前端18 小时前
ios接入穿山甲【Swift】
macos·ios·cocoa
键盘敲没电19 小时前
【iOS】UITableView性能优化
ios·性能优化·ipad
星鹿XINGLOO19 小时前
ChatGPT语音功能在iPad上支持吗?全面解答!
人工智能·安全·ios·ai·chatgpt·语音识别·ipad