【问题标题】:Lazy Image Drawing懒惰的图像绘制
【发布时间】:2011-04-26 14:19:21
【问题描述】:

我有一个对象需要按需绘制到图形上下文中,但是,内容需要时间来呈现,并且在调用对象的绘制方法时可能不可用。

这通常是如何完成的?存储对请求绘图的图形上下文或视图的引用,并在对象内部表示完全呈现时延迟绘制它?

或者是否有其他标准的可可机制来处理这个问题(例如 NSImage 在使用 NSURL 初始化时会延迟绘制)?

说明:


  • 我使用的是 MacOS,而不是 iOS
  • 使用一些NSViews -setNeedsDisplay: 不是我正在寻找的答案(NSImage 不依赖于-setNeedsDisplay:

【问题讨论】:

  • 请解释一下为什么你如此反对调用setNeedsDisplay: 这就是一个视图如何表示它已准备好被绘制。要么你要调用它,要么当你改变它的一个 ivars 时,视图会自己做。
  • 因为我想知道NSImage 在不使用视图引用和-setNeedsDisplay: 的情况下如何处理这个问题。
  • 好的,我已经尝试在更新我的答案时解决这个问题。

标签: objective-c cocoa macos appkit


【解决方案1】:

你应该考虑使用NSOperation; NSInvocationOperation,如果你有一个特定的对象进行渲染,或者NSBlockOperation,如果渲染足够简单,可以放入单个函数中。

如果您可以在实际到达视图的drawRect: 之前开始渲染,那么就这样做(也许您的应用委托在启动时就开始了该过程)。否则,检查drawRect: 内容是否可用;如果没有,则开始操作并继续绘制另一幅图。当渲染对象完成它的工作时,它可能会发布一个通知,或者,如果你给它一个对视图的引用,调用setNeedsDisplay:

根据您最后一句话,您还可以考虑您的渲染对象能够返回部分渲染的图像。我不确定您的渲染的性质,但可能会在某些点(每 n 个循环的结尾,或每 n 行像素)获得结果,将其放入单独的 NSImage 的与最终图像大小相同(必要时在末尾填充),并使该部分图像可用于视图进行绘制。

更新:NSImage 不“依赖”setNeedsDisplay: 或具有视图引用,因为它不代表屏幕的一部分。它所做的只是包含图像的数据;它只能在内部一个视图中绘制自己,然后“显示”——实际上是在屏幕上绘制。当您使用initByReferencingURL: 时,它会存储 URL,然后当另一个对象(例如包含图像并需要显示的视图)向它询问其内容时,它会执行您使用 @ 时会执行的操作987654332@,即打开文件并将其内容读入内存。不过,它不会绘制。它只会将 绘制到需要它的视图中, 该视图正在绘制。

子类化NSImage 来实现自己的延迟加载或延迟渲染可能并不容易;它使用了我认为是class cluster 一部分的辅助类,这就是为什么我建议使用一个包含并返回NSImage 的“渲染器”对象。

更多:

自定义视图的drawRect:

- (void)drawRect:(NSRect)dirtyRect {
    NSLog(@"Entered: %@", NSStringFromSelector(_cmd));
    // Use a nice big image of the Milky Way -- this is about 5MB
    NSImage * lazyImage = [[[NSImage alloc] initByReferencingURL:
                            [NSURL URLWithString:@"http://www.eso.org/public/archives/images/original/milkyway.jpg"]]
                           autorelease];
    NSLog(@"Image instantiated.");
    [lazyImage drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
    NSLog(@"Image drawn");  // 2 minutes later; sometimes 3 in my testing
    [[NSColor yellowColor] set];
    [[NSBezierPath bezierPathWithRect:NSInsetRect([self bounds], 4, 4)] stroke];
    NSLog(@"Bezier path drawn; exiting drawRect.");
}

由此产生的日志输出;请注意,实例化非常快,但加载和绘制需要两分钟,在此期间什么都没有绘制,应用程序什么也不做(旋转的沙滩球):

2011-04-27 21:33:00.899 SetNeedsDisplay[80162:a0b] 输入:drawRect: 2011-04-27 21:33:00.901 SetNeedsDisplay[80162:a0b] 图像实例化。 2011-04-27 21:34:57.911 SetNeedsDisplay[80162:a0b] 绘制的图像。 2011-04-27 21:34:57.912 SetNeedsDisplay[80162:a0b] 绘制的贝塞尔路径;退出drawRect。

【讨论】:

  • 注意我说的是MacOS;我什至没有意识到 Wolfgang 的建议是针对 iOS 的。
  • 问题是,NSImage 会延迟绘图,即使您使用 NSURL 对其进行初始化,该NSURL 引用了首先必须从 Internet 下载的文件。而且它不会阻止绘图方法(据我所知)。但我现在会接受你的回答。
  • @Erik:延迟绘图意味着“仅在最后可能的时刻进行绘制”,而不是“在获得所需的所有数据之前不绘制”。这不是懒惰的绘图。它没有任何地方可以自己绘制 to。它懒惰地加载;直到它被要求画画。您必须发布您的代码才能让我确切知道您在说什么,但是如果您尝试绘制一个使用 initByReferencingURL: 创建的 NSImage,它肯定会阻止一个绘图方法,直到它加载;请参阅我的答案的进一步更新。
  • 好吧,我说我不确定,但它总是似乎对我没有阻碍。但我会相信你的话:)。
  • @Erik:不要相信我的话:测量! ;) 这就是我发布该代码的原因。比我正确更重要的是:我希望这真的能帮助您解决问题。
【解决方案2】:

出于这些目的,我喜欢 EGOImageView 类。它非常易于使用,并且包含一些不错的奖励,例如缓存机制。

在此处阅读有关 EGOImageView 的更多信息(包括来自 Github 的下载链接):

http://developers.enormego.com/view/what_if_images_on_the_iphone_were_as_easy_as_html

【讨论】:

  • 图像视图似乎通过回调处理它,当下载完成时在视图上调用-setNeedsDisplay:。我希望有一个更通用的解决方案(比如我提到的NSImage 绘图)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
  • 1970-01-01
相关资源
最近更新 更多