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

如何在iOS应用程序中正确使用setNeedsDisplayInRect?,ios,swift,xcode7,Ios,Swift,Xcode7,我在Yosemite 10.10.5和Xcode 7上,使用Swift制作了一款针对iOS 8及以上版本的游戏 编辑:更多可能有用的细节:这是一款2D益智/街机游戏,玩家在游戏中移动石头以匹配它们。根本没有3D渲染。画画已经太慢了,我甚至还没有看到碎片爆炸。还有一个级别的淡入,非常令人担忧。但到目前为止,这些都在模拟器上。我还没有一台真正的iPhone可以测试,我打赌实际的设备至少会快一点 我有自己的Draw2D类,它是UIView的一种类型,设置如中所示。我有一个NSTimer,它在Draw2

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

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

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

[setNeedsDisplay];//它调用drawRect,这是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
就是这样做的

我找不到这方面的任何教程或好的示例代码。我找到的最接近的是苹果的文档,但它没有解释,除其他外,如何得到一个所有脏矩形的列表。如果在每次调用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)];
}
但是,我不知道脏矩形的内置列表的名称。我发现说方法名是
getrectsbeingdraw
,但那是针对MacOSX的。它在iOS中不存在


有人能帮我吗?我做得对吗?我对Mac和iOS还是相当陌生。

如果可能的话,你应该避免覆盖
drawRect
。现有的视图/技术利用任何硬件功能使事情比在图形环境中手动绘制快得多,包括缓冲视图内容、使用GPU等。这在“iOS视图编程指南”中重复了很多次

如果你有一个背景和其他对象在上面,你可能应该使用单独的视图或图层,而不是重画它们

您还可以考虑诸如SpriteKit、ScEnKIT、OpenGL ES等的技术

除此之外,我不太确定我是否理解你的问题。调用
setNeedsDisplayInRect
时,它会将该矩形添加到需要重新绘制的矩形中(可能与列表中已有的矩形合并)
drawRect:
稍后将被调用,一次绘制一个矩形

setNeedsDisplayInRect
/
drawRect:
分离的全部目的是确保将重画视图给定部分的多个请求合并在一起,并且每个重画周期只进行一次绘制

您不应该在
drawRect:
中调用您的
scr\u步骤
方法,因为它可能在一个循环重画循环中被多次调用。这一点在“iOS视图编程指南”(重点内容)中有明确说明:

drawRect:方法的实现应该只执行一个操作 事情:画你的内容此方法不是要更新的位置 您的应用程序的数据结构或执行任何不相关的任务 到绘图。它应该配置绘图环境,绘制 内容,并尽快退出。如果您的drawRect:method 可能会经常打电话,你应该尽你所能 优化绘图代码,每次尽可能少地绘图 方法被调用

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

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


不知道您的视图显示了什么,您调用的各种方法做了什么,实际需要时间做什么,很难提供更多关于您可以做什么的见解。根据您的实际需要提供更多详细信息,我们可能会提供帮助。

好的,我通过让NSTimer调用scr_run_gameloop{sc来拆分更新和绘制代码