避免 UIView 创建非常大的绘图画布

Posted

技术标签:

【中文标题】避免 UIView 创建非常大的绘图画布【英文标题】:Avoiding UIView to create very large drawing canvas 【发布时间】:2011-08-26 12:57:22 【问题描述】:

我从 UIView 派生了一个类,只是意识到由于内存的原因,它的大小确实存在限制。我在 UIScrollView 中有这个 UIView。

有没有办法让我在滚动视图中放入不是 UIView 派生类但我仍然可以在其中绘制并且可以非常大的滚动视图?

我不介意必须响应暴露矩形事件,就像使用传统窗口系统时那样。

谢谢。

【问题讨论】:

【参考方案1】:

UIScrollView 中的内容必须是 UIViews,出于内存原因,它们的大小受到限制。出于性能原因,UIView 维护一个位图后备存储,因此它必须按比例分配内存。

处理此问题的常用方法是生成多个UIViews,并在用户滚动时将它们交换出去。另一个版本是使用CATiledLayer。但是,它们都没有给你“巨型画布”绘图模型。您可以根据需要分解并绘制它们。不过,这是通常的方法。

如果你真的想要一个巨大的画布,我的推荐是CGPDFContext。对这些有丰富的现有支持,特别是使用UIWebView(请记住,您可以打开data: URI 以避免从磁盘读取文件)。您可以通过应用仿射变换然后CGContextDrawPDFPage 直接绘制其中的一部分。 CGBitmapContext 是另一种方法,但它可能需要更多内存来进行少量绘图。

【讨论】:

【参考方案2】:

所以您在UIScrollView 中有一个UIView,但您希望您的UIView 具有非常大的界限(即,它与您的UIScrollViewcontentSize 的大小相匹配)。但是您不想在每次需要显示时都绘制整个UIView,也不能一次将其全部内容放入内存中。

让您的UIView 使用CAScrollLayer 支持,如下所示:

// MyCustomUIView.m

+ (Class) layerClass

    return [CAScrollLayer class];

当用户滚动包含您的UIViewUIScrollView 时,添加一个更新滚动位置的方法:

// MyCustomUIView.m

- (void) setScrollOffset:(CGPoint)scrollOffset

    CAScrollLayer *scrollLayer = (CAScrollLayer*)self.layer;

    [scrollLayer scrollToPoint:scrollOffset];

确保在绘制UIView 时,只绘制提供给您的CGRect 中包含的部分:

- (void)drawRect:(CGRect)rect

    // Only draw stuff that lies inside 'rect'
    // CGRectIntersection might be handy here!

现在,在您的 UIScrollViewDelegate 中,您需要在父 UIScrollView 更新时通知您的 CAScrollLayer 支持视图:

// SomeUIScrollViewDelegate.m

- (void) scrollViewDidScroll:(UIScrollView *)scrollView

    // Offset myCustomView within the scrollview so that it is always visible
    myCustomView.frame = CGRectMake(scrollView.contentOffset.x, 
                                    scrollView.contentOffset.y, 
                                    scrollView.bounds.size.width, 
                                    scrollView.bounds.size.height);

    // "Scroll" myCustomView so that the correct portion is rendered
    [myCustomView setScrollOffset:self.contentOffset];

    // Tell it to update its display
    [myCustomView setNeedsDisplay];

您也可以使用CATiledLayer,这更容易,因为您不必跟踪滚动位置——相反,您的drawRect 方法将根据需要在每个图块中调用。但是,这会导致您的视图慢慢淡入。如果您打算缓存部分视图并且不介意缓慢的更新,这可能是可取的。

【讨论】:

以上是关于避免 UIView 创建非常大的绘图画布的主要内容,如果未能解决你的问题,请参考以下文章

如何避免使用领域驱动设计拥有非常大的对象

那里已经有 AngularJS 的画布绘图指令了吗?

如何使用递归函数(C#,D&C)避免非常大的数字的溢出乘法

媒体层:Core Graphics(绘图)

如何避免带有非常大的 IN 表的 dbsql_stmnt_too_large 转储?

SurfaceView的使用