什么是强制UIView重绘最强大的方法?
我有一个项目列表的UITableView。 select一个项目推一个viewController,然后继续执行以下操作。 从方法viewDidLoad我发射一个URLRequest为我的子视图所需的数据 – 一个UIView子类与drawRect覆盖。 当数据从云端到达时,我开始构build我的视图层次结构。 有问题的子类传递数据,它的drawRect方法现在拥有了它需要呈现的所有东西。
但。
因为我不明确调用drawRect – Cocoa-Touch处理 – 我没有办法通知Cocoa-Touch,我真的很想要这个UIView子类来呈现。 什么时候? 现在会很好!
我试过[myView setNeedsDisplay]。 有时这种工作。 很斑点。
我已经摔了几个小时。 有谁可以请我提供一个坚实的,有保证的方法来强制UIView重新渲染。
以下是将数据提供给视图的代码片段:
// Create the subview self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease]; // Set some properties self.chromosomeBlockView.sequenceString = self.sequenceString; self.chromosomeBlockView.nucleotideBases = self.nucleotideLettersDictionary; // Insert the view in the view hierarchy [self.containerView addSubview:self.chromosomeBlockView]; [self.containerView bringSubviewToFront:self.chromosomeBlockView]; // A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-) [self.chromosomeBlockView setNeedsDisplay];
干杯,道格
强制UIView
重新呈现的有保证的,坚如磐石的方式是[myView setNeedsDisplay]
。 如果遇到问题,您可能会遇到以下其中一个问题:
-
在实际拥有数据之前调用它,或者
-drawRect:
过度caching某些内容。 -
您期望在您调用此方法的时刻绘制视图。 使用Cocoa绘图系统故意没有办法要求“现在画第二个”。 这将扰乱整个视图合成系统,垃圾性能和可能造成各种制造。 只有这样才能说“这需要在下一个抽签周期中”。
如果你需要的是“一些逻辑,绘制,一些更多的逻辑”,那么你需要把“更多的逻辑”放在一个单独的方法中,并使用-performSelector:withObject:afterDelay:
调用它-performSelector:withObject:afterDelay:
延迟0在下一个抽签周期之后放上“更多的逻辑”。 看到这个问题的例子,这种代码,以及可能需要的情况下(虽然通常是最好寻找其他解决scheme,如果可能的话,因为它使代码复杂)。
如果你不认为事物正在被绘制,在-drawRect:
放置一个断点-drawRect:
看看你什么时候被调用。 如果你正在调用-setNeedsDisplay
,但是-drawRect:
在下一个事件循环中没有被调用,那么就深入你的视图层次结构,并确保你没有尝试智取某个地方。 过分的聪明是我经验不好的第一个原因。 当你认为如何欺骗系统做你想做的事情时,你通常会把它做到你不想要的地步。
在调用setNeedsDisplay和drawRect之间有一个很大的延迟:(5秒)。 事实certificate,我在不同于主线程的线程中调用了setNeedsDisplay。 把这个电话转到主线后,延迟就消失了。
希望这个对你有帮助。
在退还保证,钢筋混凝土坚实的方式来强制视图同步绘制 (在返回到调用代码之前)是configurationCALayer
与您的UIView
子类的交互。
在你的UIView子类中,创build一个- display
方法,告诉图层“ 是的,它需要显示 ”,然后“ 做到这一点 ”:
/// Redraws the view's contents immediately. /// Serves the same purpose as the display method in GLKView. /// Not to be confused with CALayer's `- display`; naming's really up to you. - (void)display { CALayer *layer = self.layer; [layer setNeedsDisplay]; [layer displayIfNeeded]; }
还要实现一个- drawLayer:inContext:
方法,它会调用你的私有/内部绘图方法(因为每个UIView都是一个CALayerDelegate) :
/// Called by our CALayer when it wants us to draw /// (in compliance with the CALayerDelegate protocol). - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context { UIGraphicsPushContext(context); [self internalDrawWithRect:self.bounds]; UIGraphicsPopContext(); }
并创build您的自定义- internalDrawWithRect:
方法,以及故障安全- drawRect:
::
/// Internal drawing method; naming's up to you. - (void)internalDrawWithRect:(CGRect)rect { // @FILLIN: Custom drawing code goes here. // (Use `UIGraphicsGetCurrentContext()` where necessary.) } /// For compatibility, if something besides our display method asks for draw. - (void)drawRect:(CGRect)rect { [self internalDrawWithRect:rect]; }
现在只要你真的需要绘制,就调用[myView display]
。 - display
将告诉CALayer
displayIfNeeded
,它将同步callback到我们的- drawLayer:inContext:
然后在- internalDrawWithRect:
进行绘制- internalDrawWithRect:
在继续之前更新绘制到上下文中的视觉效果。
这种方法类似于上面的@ RobNapier,但是除了- setNeedsDisplay
之外,还具有调用- displayIfNeeded
的优点,这使得它是同步的。
这是可能的,因为CALayer
比UIView
显示更多的绘图function。图层比视图更低级别,并且为了布局中高度可configuration的绘图的目的而明确地devise,并且(像Cocoa中的许多事物)被devise为被使用灵活地(作为父母class级,或作为一个委托人,或作为一个桥梁,其他绘图系统,或只是自己)。
有关CALayer
的可configuration性的更多信息可以在Core Animation Programming Guide的“ 设置层对象”部分find。
我遇到了同样的问题,所有来自SO或Google的解决scheme都不适合我。 通常情况下, setNeedsDisplay
不起作用,但是当它不…
我试图从每一个可能的线程和东西的每一个可能的方式调用setNeedsDisplay
视图 – 仍然没有成功。 我们知道,正如罗布所说的那样
“这需要在下一个绘制周期中绘制。”
但由于某种原因,这一次不会得出结论。 我发现的唯一解决方法是在一段时间后手动调用它,让任何阻止绘制的东西消失,如下所示:
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.005 * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { [viewToRefresh setNeedsDisplay]; });
如果您不需要经常重绘视图,这是一个很好的解决scheme。 否则,如果你正在做一些移动(动作)的东西,调用setNeedsDisplay
通常没有问题。
我希望这会帮助那些像我一样迷失在那里的人。
那么我知道这可能是一个很大的变化,甚至不适合你的项目,但是你有没有考虑过在推进数据之前不要进行推送 ? 这样,您只需要画一次视图,用户体验也会更好 – 推送将会在已经加载的情况下移动。
你这样做的方式是在你asynchronous要求数据的UITableView
didSelectRowAtIndexPath
。 一旦你收到响应,你手动执行segue并将数据传递给prepareForSegue
的viewController。 同时你可能想要显示一些活动指标,对于简单的加载指标检查https://github.com/jdg/MBProgressHUD