【问题标题】:In which case does drawRect not receive the full frame of the UIView?什么情况下drawRect没有收到UIView的全帧?
【发布时间】:2025-12-17 23:25:02
【问题描述】:

我正在编写一个补丁托架控件,并且我正在使用 UIViews 来绘制补丁之间的链接。

这些链接是大型 UIView 的子视图,它本身就是 UIScrollView 的子视图。

链接可能会变得非常大,通常是屏幕大小的四倍。 当其中一个结束补丁移动时,需要重新绘制链接。

但是,在某些情况下,只有部分链接是可见的。

Instruments 表示大部分时间都花在了我的 QCLink drawRect 方法上。

我已检查每次需要重绘此 QCLink 时,是否使用 QCLink 的完整边界调用 drawRect 方法。

在这种情况下我应该只需要重绘 UIView 的一部分(drawRect 中的 rect 参数:)吗?

以下是一些屏幕截图,可帮助您了解我面临的问题。

【问题讨论】:

    标签: ios cocoa-touch uiview


    【解决方案1】:

    它始终是完整的矩形。改用CAShapeLayers 怎么样?

    类似:

    CAShapeLayer * link = [ CAShapeLayer layer ] ;
    link.strokeColor = [ [ UIColor greenColor ] CGColor ] ;
    
    CGMutablePathRef p = CGPathCreateMutable() ;
    CGPathMoveToPoint( p, NULL, start ) ;
    CGPathAddLineToPoint( p, NULL, end ) ;
    link.path = path ;
    
    [ parentView.layer addSublayer:link ] ;
    

    【讨论】:

    • 您的意思是不使用drawRect,而是在QCLink 支持层中添加一个CAShapeLayer 子层?我更愿意将链接保留为 UIView 子类。
    • 是的——如果你避开-drawRect,你的绘图会更快。 IE。用路径(在形状图层中)和图像(在普通图层中)绘制所有内容。我认为没有必要将您的链接放在UIView 中——这会使您的代码更加复杂,而且我猜您没有使用任何UIView 功能...
    • 如果你需要与链接交互,我只会把它放在 UIView 中——然后你可以简单地创建一个 UIView 子类,将 CAShapeLayer 添加到它自己的层。跨度>
    • 我最终将需要一些用户交互,我不想最终从 CALayer 开始重新设计 UIViews 行为。
    • 这里有一些权衡。使用形状图层将具有将线条绘制与节点绘制分离的优势。 (如果你移动一个节点,你只需要渲染改变的线。)不幸的是,如果你在一个节点上有n 线,你将有n 纹理来重绘和合成,你最好使用@987654330 @。据我所知,这也不能真正解决问题。对于多屏幕长的线条,系统仍然需要光栅化巨大的纹理来处理它。
    【解决方案2】:

    您可能正在寻找CATiledLayer。平铺层仅绘制屏幕上需要渲染的部分,使其非常适合屏幕可能太大或您要捏合缩放的视图。它们是 UIWebView 等视图呈现方式的核心。

    要切换视图以使用平铺层,您只需声明此方法:

    + (Class)layerClass
    {
        return [CATiledLayer class];
    }
    

    然后您将开始看到对 drawRect: 的调用,其值为 (0, 0, 256, 256)(0, 256, 256, 256)...

    在移动东西时,您可以通过调用setNeedsDisplayInRect: 而不是setNeedsDisplay 来获得额外的性能优势。这会将绘图限制为无效的矩形。

    【讨论】:

    • 您的意思是将链接替换为 CATiledLayer 子类,还是包含链接和节点的整个视图?
    • 整个视图。您不需要为每个链接使用平铺层。
    • 这取决于。就个人而言,我会将所有内容渲染到一个视图上;调整视图大小并完整绘制它的性能影响可能比仅重绘所有内容(如果它是一个视图)更糟糕。如果您确实将线条保留为自己的视图,则应将它们平铺以避免在屏幕外绘制的成本。
    • 假设QCLink 只是在drawRect: 中画了一条线,你根本不需要担心修改drawRect:。您只需添加layerClass
    【解决方案3】:

    drawRect在什么情况下收不到UIView的全帧?

    您的绘图实现应始终准备好绘制您的视图的一部分。对于某些任务,默认的剪辑效果很好。

    只需遵循视图失效过程——如果您使矩形失效,那么视图系统会遍历视图并要求它们绘制该矩形中的内容(考虑不透明度等因素)。该矩形可能是(由)矩形的联合,但也可能被系统裁剪。

    因此,您可能正在过度绘制或进行多余的绘制 - 考虑如何减少这种情况。对于初学者,您可能希望将所有电线放在一个视图中,并尽一切可能将不透明的表面最小化。在那之后,你应该确定你在哪里透支。 Quartz Debug 可以指出这些多余的绘制。您应该使用setNeedsDisplayInRect: 而不是setNeedsDisplay,尤其是在绘制时间很关键的情况下。

    【讨论】:

      最近更新 更多