使用NSImageView快速连续显示多个图像

时间:2011-06-30 22:20:24

标签: objective-c cocoa performance nstimer nsimageview

我有一个应用程序,在一个窗口中,有一个NSImageView。用户应该能够将任何文件/文件夹(不仅仅是图像)拖放到图像视图中,因此我将NSImageView类子类化,以添加对这些类型的支持。

我选择NSImageView而不是普通视图的原因是因为当用户将鼠标悬停在准备丢弃的文件上时,我还想显示一个动画(例如,一个箭头指向下方并向上和向下)。我的问题是:执行此操作的最佳方式(最有效,最快,最少CPU使用率等)是什么?

事实上,我已经做过了,但是让我问这个问题的是,当我设置图像以低于0.02秒的速率变化时,它开始滞后。我是这样做的:

在NSImageView子类中:

  • 有一个ivar:NSTimer * animTimer;
  • 覆盖awakeFromNib,调用[super awakeFromNib]并使用NSImage将图像加载到一个数组(大约45个图像)
  • 每当用户输入文件时,启动animTimer,频率= 0.025(少且滞后),以及设置数组中下一个图像的选择器(称为drawNextImage)
  • 每当用户退出或结束拖放时,请调用[animTimer invalidate]以停止更新图像

以下是我在子类中设置图像的方法:

- (void)drawNextImage
{
    currentImageIndex++; // ivar / kNumberDNDImages is a constant defined as 46
    if (currentImageIndex >= kNumberDNDImages) { currentImageIndex = 0;}
    [super setImage: [imagesArray objectAtIndex: currentImageIndex]]; // imagesArray is ivar
}

那么,我怎么能这么快?我希望频率大约是0.01秒,但是小于0.025滞后,所以这就是我现在所设定的。哦,我的图像是正确的大小(+或 - 一个像素或什么),它们在.png(我需要透明度 - 例如,jpegs不会这样做。)

编辑:

我试图遵循NSResponder的建议,并将我的方法更新为:

- (void)drawNextImage
{
    currentImageIndex++;
    if (currentImageIndex >= kNumberDNDImages) { currentImageIndex = 0;}
    NSRect smallImgRect;
    smallImgRect.origin = NSMakePoint(kSmallImageWidth * currentImageIndex, [self.bigDNDImage size].height); // Up left corner - ??
    smallImgRect.size = NSMakeSize(kSmallImageWidth, [self.bigDNDImage size].height);

    // Bottom left corner - ??
    NSPoint imgPoint = NSMakePoint(([self bounds].size.width - kSmallImageWidth) / 2, 0);

    [bigDNDImage drawAtPoint: imgPoint fromRect: smallImgRect operation: NSCompositeCopy fraction: 1];
}

我还将此方法和其他拖放方法从NSImageView子类移动到我已经拥有的NSView子类。除了超类和这种方法之外,一切都完全相同。我还修改了一些常量。

在我对此的早期测试中,我收到了一些错误/警告消息,这些消息并没有停止执行与NSGraphicsContext或其他东西的谈话。这些已经消失了,但你知道。我完全不知道他们为什么出现以及他们的意思。如果他们再次出现我会担心他们,而不是现在:)

编辑2:

这就是我现在正在做的事情:

- (void)drawNextImage
{
    currentImageIndex++;
    if (currentImageIndex >= kNumberDNDImages) { currentImageIndex = 0;}
    [self drawCurrentImage];
}
- (void)drawCurrentImage
{
    NSRect smallImgRect;
    smallImgRect.origin = NSMakePoint(kSmallImageWidth * currentImageIndex, 0); // Bottom left, for sure
    smallImgRect.size = NSMakeSize(kSmallImageWidth, [self.bigDNDImage size].height);

    // Bottom left as well
    NSPoint imgPoint = NSMakePoint(([self bounds].size.width - kSmallImageWidth) / 2, 0);

    [bigDNDImage drawAtPoint: imgPoint fromRect: smallImgRect operation: NSCompositeCopy fraction: 1];
}

这里的问题是调用drawRect时调用drawCurrentImage(参见,它实际上比我想象的更容易解决)。

现在,我必须说我没有尝试使用合成图像,因为我找不到一种快捷方式以我想要的方式合并40多个图像(一个接着另一个)。但对于那些不感兴趣的人,我修改了它来做与我的NSImageView子类相同的事情(从数组中读取40多个图像并显示它们)并且我没有发现减速:NSView与NSImageView一样低于0.025。我在使用核心动画时也发现了一些问题(图像是在奇怪的地方绘制而不是我告诉她的地方)和一些警告谈论NSGraphicsContext,我根本不知道如何解决(我是一个完整的)在使用Objective-C的工具进行绘图等时使用了noob。所以目前我正在使用NSImageView,除非我找到一种方法来合并所有这些图像并尝试使用NSView。

2 个答案:

答案 0 :(得分:2)

核心动画可能是最快的,因为它会在GPU上完成所有工作。为每个图像创建一个图层,将每个图层的contents设置为您可以从每个图像创建的CGImage,将它们全部添加为单个顶级图层的子图层host the top-level layer in a plain NSView,然后只需切换每个图像依次为图层的hidden属性。

答案 1 :(得分:0)

我可能会将所有组件图像绘制成一个长图像,然后使用-drawAtPoint:fromRect:operation:fraction:将视图绘制到视图中。不过,我确信你可以通过使用OpenGL来加快速度。