如何正确使用setNeedsDisplayInRect for iOS应用程序?

时间:2016-01-11 07:21:25

标签: ios swift xcode7

我使用的是Yosemite 10.10.5和Xcode 7,使用Swift制作针对iOS 8及以上版本的游戏。

编辑:可能有用的更多细节:这是一款2D拼图/街机游戏,玩家可以移动石头来匹配它们。根本没有3D渲染。绘图已经太慢了,我还没有得到碎片爆炸。还有一个淡入淡出的水平,非常令人担忧。但到目前为止,这一切都在模拟器上。我还没有真正的iPhone进行测试,我打赌实际的设备至少会快一点。

我有自己的Draw2D类,它是一种UIView,在this tutorial中设置。我有一个NSTimer,它在Draw2D中启动以下调用链:

[setNeedsDisplay]; // which calls drawRect, which is the master draw function of Draw2D

drawRect(rect: CGRect)
{
  scr_step(); // the master update function, which loops thru all objects and calls their individual update functions. I put it here so that updating and drawing are always in sync

  CNT = UIGraphicsGetCurrentContext(); // get the curret drawing context

  switch (Realm) // based on what realm im in, call the draw function for that realm
  {
    case rlm.intro: scr_draw_intro();
    case rlm.mm: scr_draw_mm();
    case rlm.level: scr_draw_level(); // this in particular loops thru all objects and calls their individual draw functions

    default: return;
  }

  var i = AARR.count - 1; // loop thru my own animation objects and draw them too, note it's iterating backwards because sometimes they destroy themselves
  while (i >= 0)
  {
    let A = AARR[i];
    A.scr_draw();

    i -= 1;
  }
}

所有的绘图都很好,但很慢。

问题是现在我想优化绘图。我想只绘制需要绘制的脏矩形,不是整个屏幕,这是setNeedsDisplay正在做的事情。

我找不到任何教程或良好的示例代码。我发现的最接近的是苹果的文档here,但它没有解释如何获得到目前为止所有脏矩形的列表。如果在每次调用drawRect结束时自动清除脏矩形列表,它也没有显式声明

它也没有解释我是否必须根据矩形手动剪辑所有绘图。我在网络上发现了有关这方面的相互矛盾的信息,显然不同的iOS版本做的不同。特别是,如果我要手动剪辑东西,那么我首先看不到苹果核心功能的重点。我可以维护自己的矩形列表,并手动将每个绘图目标矩形与脏矩形进行比较,看看我是否应该绘制任何东西。然而,这将是一个巨大的痛苦,因为我在每个级别都有背景图片,我会在每个移动物体后面画一块。 我真正希望的是使用setNeedsDisplayInRect 的正确方法让核心框架对下一个绘制周期中绘制的所有内容进行自动裁剪,以便它自动仅绘制该部分背景加上顶部的移动物体。

所以我尝试了一些实验:首先在我的一系列宝石中:

func scr_draw_stone()
{
  // the following 3 lines are new, I added them to try to draw in only dirty rectangles
  if (xvp != xv || yvp != yv) // if the stone's coordinates have changed from its previous coordinates
  {
    MyD.setNeedsDisplayInRect(CGRectMake(x, y, MyD.swc, MyD.shc)); // MyD.swc is Draw2D's current square width in points, maintained to softcode things for different screen sizes.
  }

  MyD.img_stone?.drawInRect(CGRectMake(x, y, MyD.swc, MyD.shc)); // draw the plain stone
  img?.drawInRect(CGRectMake(x, y, MyD.swc, MyD.shc)); // draw the stone's icon
}

这似乎没有任何改变。事情和以前一样缓慢。那么我把它放在括号中:

[MyD.setNeedsDisplayInRect(CGRectMake(x, y, MyD.swc, MyD.shc))];

我不知道括号是做什么的,但我原来的setNeedsDisplay就像他们在教程中所说的那样放在括号中。所以我在我的石头物体中尝试过它,但它也没有效果。

那么我需要做些什么才能使setNeedsDisplayInRect正常工作?

现在,我怀疑在我的主绘制功能中需要一些条件检查,例如:

if (ListOfDirtyRectangles.count == 0)
{
  [setNeedsDisplay]; // just redraw the whole view
}
else
{
  [setNeedsDisplayInRect(ListOfDirtyRecangles)];
}

但是我不知道内置脏矩形列表的名称。我发现this说方法名称为getRectsBeingDrawn,但这适用于Mac OSX。它在iOS中不存在。

任何人都可以帮助我吗?我是否在正确的轨道上?我还是Mac和iOS的新手。

1 个答案:

答案 0 :(得分:7)

如果可能的话,你应该真的避免覆盖drawRect。现有的视图/技术利用任何硬件功能,比手动绘制图形上下文更快,包括缓冲视图内容,使用GPU等。这在"视图中重复多次iOS编程指南"。

如果你有一个背景和其他对象,你应该为那些使用单独的视图或图层而不是重绘它们。

您也可以考虑使用SpriteKit,SceneKit,OpenGL ES等技术

除此之外,我不太确定我理解你的问题。当你调用setNeedsDisplayInRect时,它会将那个rect添加到需要重绘的那些(可能与列表中已有的矩形合并)。稍后会稍后调用drawRect:来逐个绘制这些矩形。

setNeedsDisplayInRect / drawRect:分离的重点是确保将重绘视图的给定部分的多个请求合并在一起,并且每次重绘周期仅绘制一次。

您不应在scr_step中调用drawRect:方法,因为它可能会在循环重绘周期中多次调用。这在“适用于iOS的视图编程指南”和#34; (强调我的):

  

drawRect:方法的实现应该只做一个   事:吸引你的内容。 此方法不是要更新的地方   您的应用程序的数据结构或执行任何不相关的任务   绘图。它应该配置绘图环境,绘制你的   内容,并尽快退出。如果你的drawRect:方法   可能会被频繁调用,你应该尽一切努力   优化您的绘图代码,每次尽可能少地绘制   该方法被称为。

关于剪辑,drawRect的文档说明:

  

您应该将任何绘图限制为rect中指定的矩形   参数。此外,如果视图的opaque属性设置为   是的,你的drawRect:方法必须完全填充指定的矩形   不透明的内容。

不知道您的观点显示的内容,您调用的各种方法,实际需要时间的内容,难以提供更多有关您可以执行的操作的信息。提供更多有关您实际需求的详细信息,我们可能会提供帮助。