如何在iOS中使用Swift实现图像逐像素慢速渲染
iOS平台Swift实现逐像素慢速图像渲染方案
核心实现逻辑
要实现逐像素慢速铺开显示完整图像的效果,不能依赖系统自带的转场、蒙版动画(这类动画是系统光栅化处理,无法精准控制逐像素的渲染速率),核心思路是手动操作图像的底层像素数据,按固定节奏逐步把已渲染的像素范围合成到显示视图上:
- 提前加载目标图像的全量像素数据到内存,避免渲染过程中重复解码图像产生性能开销
- 用
CADisplayLink做逐帧刷新驱动,每帧按设定速率推进渲染进度 - 每帧根据当前进度截取对应范围的像素,绘制成临时图像展示,直到进度达到100%覆盖全图
关键代码实现
先创建自定义UIView子类承载渲染逻辑,提前定义核心属性:
class PixelRenderView: UIView { // 待渲染的目标图像 var targetImage: UIImage? // 当前渲染进度 0~1 private var renderProgress: CGFloat = 0 // 每帧推进的进度步长,数值越小渲染速度越慢 private let progressStep: CGFloat = 0.002 // 逐帧刷新的驱动定时器 private var displayLink: CADisplayLink? // 存储全量图像像素的缓冲区 private var pixelBuffer: UnsafeMutablePointer<UInt8>? private var imageWidth: Int = 0 private var imageHeight: Int = 0 }
第一步:在开始渲染前,一次性把原图的像素数据读取到缓冲区,避免重复解码:
func preparePixelData() { guard let cgImage = targetImage?.cgImage else { return } imageWidth = cgImage.width imageHeight = cgImage.height let colorSpace = CGColorSpaceCreateDeviceRGB() let bytesPerPixel = 4 let bytesPerRow = bytesPerPixel * imageWidth // 按图像尺寸开辟像素内存 pixelBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: imageWidth * imageHeight * bytesPerPixel) // 把原图像素写入开辟的缓冲区 guard let context = CGContext( data: pixelBuffer, width: imageWidth, height: imageHeight, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue ) else { return } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: imageWidth, height: imageHeight)) }
第二步:启动逐帧渲染循环,每帧更新进度并重绘视图:
func startRender() { preparePixelData() renderProgress = 0 // 初始化CADisplayLink绑定屏幕刷新节奏 displayLink = CADisplayLink(target: self, selector: #selector(onFrameRefresh)) displayLink?.add(to: .main, forMode: .common) } @objc private func onFrameRefresh() { // 按步长推进渲染进度 renderProgress += progressStep // 进度满值后停止刷新 if renderProgress >= 1 { renderProgress = 1 displayLink?.invalidate() displayLink = nil } // 触发视图重绘 setNeedsDisplay() }
第三步:在draw(_ rect:)方法里,按当前进度截取对应范围的像素绘制:
开发注意:不要在draw方法里遍历全量像素做逐点绘制,直接复用已经加载好的像素缓冲区,按当前进度计算可绘制的区域范围,生成临时图像展示,避免CPU占用过高导致界面卡顿
效果调试要点
- 调整
progressStep的数值匹配预期速度:比如progressStep设为0.002时,按60帧刷新率计算,完整渲染完整张图大概需要8秒,和慢速逐像素展示的节奏匹配 - 如果要实现散点逐步聚合铺满的视觉效果,可以在渲染初期(进度<50%)把像素绘制顺序从线性铺开改成随机散点选取,等进度过半后再按区域顺序补全剩余像素块,完全对齐预期的动效节奏
- 渲染结束后记得手动释放
pixelBuffer的内存,调用deallocate()方法回收资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Abdul Rehman
相关产品推荐
相关产品推荐

