首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何正确地将setNeedsDisplayInRect用于iOS应用程序?

如何正确地将setNeedsDisplayInRect用于iOS应用程序?
EN

Stack Overflow用户
提问于 2016-01-11 07:21:24
回答 1查看 1.1K关注 0票数 10

我正在使用Yosemite 10.10.5和Xcode 7,使用Swift制作一个针对iOS 8和更高版本的游戏。

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

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

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

代码语言:javascript
复制
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?结束时自动清除,它也不会显式地声明drawRect?。

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

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

代码语言:javascript
复制
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正常工作,我需要做什么呢?

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

代码语言:javascript
复制
if (ListOfDirtyRectangles.count == 0)
{
  [setNeedsDisplay]; // just redraw the whole view
}
else
{
  [setNeedsDisplayInRect(ListOfDirtyRecangles)];
}

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

有人能帮我吗?我这样做正确吗?我对Mac和iOS还是相当陌生的。

EN

回答 1

Stack Overflow用户

发布于 2016-01-14 23:38:35

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

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

您还可以考虑诸如SpriteKit、SceneKit、OpenGL ES等技术。

除此之外,我不太明白你的问题。当您调用setNeedsDisplayInRect时,它会将这个rect添加到那些需要重新绘制的区域(可能与列表中已经存在的矩形合并)。稍后将调用drawRect:,以便一次绘制一个矩形。

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

您不应该在scr_step中调用drawRect:方法,因为它可能在循环重绘周期中被多次调用。这一点在“iOS的视图编程指南”(强调我的观点)中有明确的表述:

drawRect:方法的实现应该做一件事情:绘制内容。此方法不是更新应用程序数据结构或执行与绘制无关的任何任务的地方。它应该配置绘图环境,绘制内容,并尽快退出。如果您的drawRect:方法可能经常被调用,那么您应该尽一切努力优化绘图代码,并在每次调用该方法时尽可能少地绘制。

关于裁剪,drawRect的文档指出:

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

不知道你的观点显示了什么,你所称的各种方法做什么,什么实际上需要时间,很难提供更多的洞察力来了解你能做什么。提供更多的细节,了解您的实际需求,我们可能会提供帮助。

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

https://stackoverflow.com/questions/34716074

复制
相关文章

相似问题

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