首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >到drawRect还是不使用drawRect (何时使用drawRect/与子视图/图像,以及为什么?)

到drawRect还是不使用drawRect (何时使用drawRect/与子视图/图像,以及为什么?)
EN

Stack Overflow用户
提问于 2013-02-02 07:03:26
回答 4查看 18.5K关注 0票数 143

为了澄清这个问题的目的:我知道如何使用子视图和使用创建复杂的视图。我正在努力充分理解什么时候和为什么要使用一个而另一个。

我也明白,提前这么多地优化是没有意义的,在做任何分析之前,做一些更困难的事情是没有意义的。考虑到我对这两种方法都很满意,现在我真的想要更深入的理解。

我的许多困惑来自于学习如何使表视图滚动性能变得非常平滑和快速。当然,此方法的原始来源来自于twitter背后的作者 for iPhone (以前的tweetie)。基本上说,要使表滚动的黄油光滑,秘诀是不要使用子视图,而是在一个自定义的uiview中完成所有的绘图。本质上似乎使用了大量的子视图来降低渲染速度,因为它们的开销很大,并且不断地对父视图进行重新组合。

公平地说,这是写在3GS是漂亮的品牌屁股新,和iDevices已经得到了更快的速度从那时起。尽管如此,对于高性能表,这种方法仍然是网际网和其他地方的定期 建议。事实上,这是在苹果的表样代码中建议的方法,已经在几个WWDC视频(面向iOS开发人员的实用绘图)和许多iOS 程序设计书籍中提出过。

甚至还有令人敬畏的工具来设计图形并为它们生成核心图形代码。

,所以一开始我被引导相信“核心图形的存在是有原因的,它很快!”

但我一想到“在可能的情况下支持核心图形”,我就会发现,drawRect经常对应用程序的响应能力差负责,对内存极其昂贵,而且真的要对CPU征税。基本上,我应该"避免覆盖drawRect“(WWDC 2012 iOS应用程序性能:图形与动画)

所以我想,就像每件事一样,事情很复杂。也许你可以帮助我自己和其他人理解什么时候和为什么要使用drawRect?

我看到了几种使用核心图形的明显情况:

  1. 您有动态数据(苹果的股票图表示例)
  2. 您有一个灵活的UI元素,不能用简单的可调整大小的映像执行。
  3. 您正在创建一个动态图形,该图形一旦呈现,将在多个地方使用。

我看到了避免核心图形的情况:

  1. 视图的属性需要单独动画。
  2. 您的视图层次相对较小,因此任何使用CG的额外工作都不值得获得。
  3. 您希望在不重新绘制整个视图的情况下更新视图的碎片。
  4. 子视图的布局需要在父视图大小更改时更新。

给你知识吧。在什么样的情况下,您才能获得drawRect/Core (这也可以用子视图完成)?是什么因素导致你做出这个决定?如何/为什么在一个自定义视图中推荐的黄油光滑的表格单元滚动,但苹果建议drawRect不要一般的性能原因?简单的背景图像(什么时候使用CG和使用可调整大小的png映像创建它们)呢?

对这一主题的深入理解可能不需要做有价值的应用程序,但我不喜欢在技术之间做出选择,除非能够解释原因。我的大脑开始生我的气。

问题更新

谢谢大家提供的信息。这里有一些澄清性的问题:

  1. 如果您正在用核心图形绘制一些东西,但是可以使用UIImageViews和预渲染的png完成相同的任务,那么您应该总是这样做吗?
  2. 一个类似的问题:尤其是在像这样的坏工具中,什么时候应该考虑在核心图形中绘制接口元素?(可能在元素的显示是可变的时候。一个有20个不同颜色变化的按钮。还有其他案件吗?)
  3. 根据我在下面的答案中的理解,在复杂的UIView呈现本身之后,有效地捕获单元格的快照位图,并在滚动和隐藏复杂视图的同时显示它,可以为表单元格获得相同的性能增益吗?很明显,有些东西必须要弄清楚。只是我有个有趣的想法。
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-02-02 18:32:34

尽可能坚持使用UIKit和子视图。您可以更有效率,并利用所有的OO机制,这些机制应该更容易维护。当您无法从UIKit中获得所需的性能时使用Core,或者您知道在UIKit中试图将绘图效果合并起来会更复杂。

一般的工作流程应该是构建带有子视图的表视图。使用仪器测量您的应用程序将支持的最老硬件上的帧速率。如果您无法获得60 get,请降至CoreGraphics。如果您已经这样做了一段时间,您就会感觉到UIKit可能是在浪费时间。

那么,为什么核心图形是快速的?

CoreGraphics不是很快。如果它一直在被使用,你可能会变慢。它是一个丰富的绘图API,它需要在CPU上完成它的工作,而不是许多卸载到GPU上的UIKit工作。如果您必须动画一个球在屏幕上移动,这将是一个可怕的想法调用一个视图60次每秒setNeedsDisplay。因此,如果您的视图中有需要单独动画的子组件,那么每个组件应该是一个单独的层。

另一个问题是,当您不使用drawRect进行自定义绘图时,UIKit可以优化股票视图,因此drawRect是没有操作的,或者使用组合方式使用快捷方式。当您重写drawRect时,UIKit必须走慢路,因为它不知道您在做什么。

在表视图单元格的情况下,这两个问题的好处可以超过它们。在第一次在屏幕上显示视图时调用drawRect后,内容将被缓存,滚动是GPU执行的简单转换。因为您处理的是单个视图,而不是复杂的层次结构,所以UIKit的drawRect优化就变得不那么重要了。因此瓶颈变成了如何优化您的核心图形绘图。

只要有可能,就使用UIKit。做最简单的工作实现。配置文件。当有激励的时候,优化。

票数 75
EN

Stack Overflow用户

发布于 2014-04-10 10:23:09

区别在于UIView和CALayer本质上处理固定图像。这些图像被上传到图形卡(如果您知道OpenGL,可以将图像看作纹理,而UIView/CALayer则作为显示这种纹理的多边形)。一旦一个图像在GPU上,它可以非常快地绘制,甚至几次,甚至(稍微降低性能),甚至在其他图像之上具有不同级别的alpha透明度。

CoreGraphics/Quartz是一个用于生成图像的API。它需要一个像素缓冲区(同样,考虑OpenGL纹理),并在其中改变单个像素。这一切都发生在RAM和CPU上,只有完成Quartz之后,图像才会被“刷新”回GPU。这个从GPU获取图像,更改它,然后上传整个图像(或者至少是相当大的一部分)回GPU的往返过程相当缓慢。另外,Quartz所做的实际绘图,虽然对于您正在做的事情来说确实是快速的,但是比GPU所做的要慢得多。

这是显而易见的,考虑到GPU主要是在不改变像素的大块移动。石英可以随机访问像素,并与网络、音频等共享CPU。此外,如果您同时使用Quartz绘制了几个元素,则当其中一个更改时,您必须重新绘制所有元素,然后上传整个数据块,而如果您更改了一个图像,然后让UIViews或CALayers将其粘贴到您的其他图像上,您可以通过将更少的数据上传到GPU而不受影响。

当您不实现-drawRect:时,大多数视图都可以被优化掉。它们不包含任何像素,所以不能画任何东西。其他视图,如UIImageView,只绘制一个UIImage (同样,它本质上是对纹理的引用,该纹理可能已经加载到GPU上)。因此,如果您使用一个UIImage绘制了5次相同的UIImageView,它只被上传到GPU一次,然后在5个不同的位置绘制到显示器上,从而节省了我们的时间和CPU。

当您实现-drawRect:时,这将导致创建一个新映像。然后在CPU上使用Quartz进行绘制。如果您在您的UIImage中绘制了一个drawRect,它可能会从GPU下载图像,将其复制到您要绘制的图像中,完成后,将图像的第二个副本上传回图形卡。所以你在设备上使用了两倍的GPU内存。

因此,绘制的最快方法通常是将静态内容与更改的内容分开(在单独的UIViews/UIView子类/CALayers中)。将静态内容加载为UIImage,并使用UIImageView绘制静态内容,并将在运行时动态生成的内容放到drawRect中。如果您的内容被反复绘制,但其本身并没有改变(例如,在相同的位置中显示的3个图标以表示某种状态),也可以使用UIImageView。

请注意:有一件事就是有太多的UIViews。特别是透明的区域会给GPU带来更大的损失,因为当显示它们时,它们需要与后面的其他像素混合。这就是为什么您可以将一个UIView标记为“不透明的”,以向GPU表明它只会抹掉图像后面的所有内容。

如果您的内容是在运行时动态生成的,但在应用程序的生存期内保持不变(例如,包含用户名的标签),那么使用Quartz、文本、按钮边框等作为背景的一部分绘制整个内容可能是有意义的。但这通常是一种优化,除非仪器应用程序以不同的方式告诉你,否则这是不需要的。

票数 58
EN

Stack Overflow用户

发布于 2013-02-03 00:43:44

,我将尝试对我从其他人的答案中推断出来的内容进行总结,并在更新原始问题时提出澄清性的问题。但我鼓励其他人继续回答,并投票给那些提供了良好信息的人。

一般方法

很明显,正如本·桑多夫斯基( Ben )在他的回答中提到的那样,通用方法应该是,“只要有可能,就使用UIKit。执行最简单的实现。配置文件。当有激励时,优化”。

为什么

  1. 在iDevice中有两个可能的瓶颈, CPU和GPU。
  2. CPU负责视图的初始绘制/呈现。
  3. GPU负责大部分动画(核心动画),图层效果,合成等。
  4. UIView有许多优化、缓存等,这些都是为处理复杂的视图层次结构而内置的。
  5. 当覆盖drawRect时,您忽略了UIView提供的许多好处,而且它通常比让UIView处理呈现更慢。

绘图单元格在一个平面UIView中的内容可以大大提高滚动表上的FPS。

正如我前面所说,CPU和GPU是两个可能的瓶颈。因为他们通常处理不同的事情,你必须注意你遇到的瓶颈。在滚动表的情况下,并不是核心图形绘制得更快,这就是为什么它可以大大改善FPS.的原因。

实际上,对于初始呈现,核心图形很可能比嵌套的UIView层次结构慢得多。然而,起伏滚动的典型原因似乎是您正在阻碍GPU,所以您需要解决这个问题。

为什么覆盖drawRect (使用核心图形)可以帮助表滚动:

据我所知,GPU不负责视图的初始呈现,而是在呈现视图之后,将纹理或位图(有时带有一些图层属性)交给GPU。然后,它负责合成位图,渲染所有这些层影响,以及大部分动画(核心动画)。

在表视图单元格中,GPU可能受到复杂视图层次结构的限制,因为它不是动画一个位图,而是动画父视图,并执行子视图布局计算、渲染层效果和合成所有子视图。因此,它不是动画一个位图,它负责的关系,一堆位图,以及它们如何相互作用,为同一像素区域。

因此,总之,使用核心图形在一个视图中绘制单元格的原因并不是因为它绘制得更快,而是因为它减少了GPU上的负载,这是在特定情况下给您带来麻烦的瓶颈。

票数 26
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14659563

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档