在 iOS 上高效绘制包含大量点的图表

Drawing chart with lots of points efficiently on iOS

我正在寻找一种方法来在 iOS 上高效地绘制包含大量点(最多 10 000 个)的折线图。该图获取实时数据,需要每秒更新多次。我正在尝试找出一种有效绘制图形的方法,这样它就不会在绘制时最大化 CPU 并阻塞主线程。

我目前正在为折线图创建一个 UIBezierPath,在后台线程中,添加所有点并在 CALayer(已启用异步绘图)中绘制它。它不是很快,最大化 CPU 并且绘图太慢 UI 变得迟钝。我正在绘制实时数据,理论上我每次都可以重复使用相同的 UIBezierPath 并仅附加新点,但是一段时间后旧值会被丢弃,这意味着必须删除已删除值的点从贝塞尔路径,这是不可能的。

我还没有在 iOS 上找到任何关于高效绘制大型数据集的信息,但我希望有一些方法可以使用 GPU 来提高性能。

几年前我遇到过这样的情况(回到 iPhone 4 天)。

我使用了一个 CGMutablePathRef,我使用 CGPathMoveToPoint 添加点作为第一个点,然后使用 CGPathAddLineToPoint 添加点。

为了获得可接受的性能,我将数据点存储在 C 数组中(而不是 NSArray)。另外,当绘制一个点时,如果它的显示坐标与最后一个绘制点相同,我就跳过了对 CGPathAddLineToPoint 的调用。 (当数据点多于像素时,通常会出现这种情况)。

我不记得确切的绘制时间,但速度出奇地快。

与其使用 UIBezierPath 绘制点,不如设置 CGBitmapContext,然后绘制点 "by hand",例如

color = (0xFF << 24) | (blue << 16) | (green << 8) | (red); // AABBGGRR
for (loop = 0; loop < count; loop++)
{
    index = indices[loop];
    self.pixels[index] = color;
}

其中 self.pixels 是您用来创建 CGBitmapContext 的缓冲区。然后,在 drawRect:

CGContextRef c = UIGraphicsGetCurrentContext();

CGImageRef image = CGBitmapContextCreateImage(self.imageContext);
CGContextDrawImage(c, self.bounds, image);
CGImageRelease(image);

我认为您会发现这比使用 CGPath 或 UIBezierPath 快得多(而且更小)。

我终于找到了用 OpenGL 绘制图形的实现:https://github.com/syedhali/EZAudio/blob/master/EZAudio/EZAudioPlotGL.m

其实很简单,而且性能提升很大。

对于配备 A7 芯片(或更高版本)的设备,用金属绘制图形可能会更快,这也应该很容易。有一些关于如何操作的指南:https://www.raywenderlich.com/77488/ios-8-metal-tutorial-swift-getting-started