我有一个带有项目列表的UITableView。选择一个项目会推送一个viewController,然后继续执行以下操作。从方法viewDidLoad我为我的子视图所需的数据启动了一个URLRequest - 一个覆盖了drawRect的UIView子类。当数据从云端到达时,我开始构建我的视图层次结构。有问题的子类传递数据,它的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];
干杯, 道格
答案 0 :(得分:186)
强制UIView
重新呈现的有保证的坚实方法是[myView setNeedsDisplay]
。如果您遇到问题,可能会遇到以下问题之一:
您在实际拥有数据之前调用它,或者-drawRect:
过度缓存了某些内容。
您希望在调用此方法时绘制视图。有意无法使用Cocoa绘图系统要求“现在就这么画”。这会破坏整个视图合成系统,垃圾性能并可能产生各种伪像。只有方法可以说“这需要在下一个绘制周期中绘制。”
如果您需要的是“某些逻辑,绘制,更多逻辑”,那么您需要将“更多逻辑”放在单独的方法中并使用-performSelector:withObject:afterDelay:
以延迟0调用它。将在下一个绘制周期后加上“更多逻辑”。请参阅this question以获取该类代码的示例,以及可能需要它的情况(尽管通常最好寻找其他解决方案,因为它会使代码复杂化)。
如果您认为事情没有被绘制,请在-drawRect:
中设置一个断点并查看您何时被召唤。如果您正在呼叫-setNeedsDisplay
,但未在下一个事件循环中调用-drawRect:
,那么请深入了解您的视图层次结构,并确保您不会在某个地方尝试智取。在我的经历中,过于聪明是导致糟糕绘画的首要原因。当你认为你最了解如何欺骗系统做你想做的事情时,你通常会把它做成你不想要的。
答案 1 :(得分:51)
我在调用setNeedsDisplay和drawRect之间有一个很大的延迟:(5秒)。事实证明我在与主线程不同的线程中调用了setNeedsDisplay。将此调用移至主线程后,延迟消失了。
希望这有一些帮助。
答案 2 :(得分:13)
退款保证,加强具体的方法强制观看 同步绘制 (在返回调用代码之前)是配置CALayer
与UIView
子类的交互。
在你的UIView子类中,创建一个- 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();
}
并创建自定义- 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
,它会同步回拨我们的- drawLayer:inContext:
并在- internalDrawWithRect:
中进行绘图,并根据绘制的内容更新视觉效果继续前进的背景。
这种方法类似于上面的@ RobNapier,但除了- displayIfNeeded
之外还有调用- setNeedsDisplay
的优势,这使得它同步。
这是可能的,因为CALayer
暴露了比UIView
更多的绘图功能 - 层比视图更低级,并且为了在布局中高度可配置的绘图而明确设计,并且(像Cocoa中的许多东西一样被设计成灵活使用(作为父类,或作为委托者,或作为其他绘图系统的桥梁,或仅仅是他们自己)。
有关CALayer
的可配置性的更多信息,请参阅Setting Up Layer Objects section of the Core Animation Programming Guide。
答案 3 :(得分:5)
我遇到了同样的问题,SO或Google的所有解决方案都不适用于我。通常,setNeedsDisplay
确实有效,但是当它不起作用时......
我试图从每个可能的线程和东西中尽可能地调用视图的setNeedsDisplay
- 仍然没有成功。我们知道,正如Rob所说,
“这需要在下一个绘制周期中绘制。”
但由于某种原因,它不会画这个时间。我发现的唯一解决方案是在一段时间后手动调用它,让任何阻止绘制的东西都消失了,就像这样:
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];
});
如果您不需要经常重绘视图,这是一个很好的解决方案。否则,如果你正在做一些移动(动作)的东西,那么只需要调用setNeedsDisplay
就没有问题。
我希望它会帮助那些迷失在那里的人,就像我一样。
答案 4 :(得分:0)
嗯,我知道这可能是一个很大的变化,甚至不适合你的项目,但你是否认为在你已经拥有数据之前没有执行推送?这样你只需要绘制一次视图,用户体验也会更好 - 推送将在已经加载的情况下移动。
执行此操作的方式是UITableView
didSelectRowAtIndexPath
您异步询问数据。收到响应后,您手动执行segue并将数据传递到prepareForSegue
中的viewController。
同时,您可能希望显示一些活动指示符,用于简单加载指标检查https://github.com/jdg/MBProgressHUD
答案 5 :(得分:0)
我正在使用 CATransaction 强制重绘:
[CATransaction begin];
[someView.layer displayIfNeeded];
[CATransaction flush];
[CATransaction commit];