迫使UIView重绘的最强大的方法是什么?

dug*_*gla 112 cocoa-touch urlrequest uiview

我有一个带有项目列表的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];
Run Code Online (Sandbox Code Playgroud)

干杯,道格

Rob*_*ier 187

强制UIView重新渲染的有保证的坚如磐石的方法是[myView setNeedsDisplay].如果您遇到问题,可能会遇到以下问题之一:

  • 您在实际拥有数据之前调用它,或者您-drawRect:正在过度缓存某些内容.

  • 您希望在调用此方法时绘制视图.有意无法使用Cocoa绘图系统要求"现在就这么画".这会破坏整个视图合成系统,垃圾性能并可能产生各种伪像.只有方法可以说"这需要在下一个绘制周期中绘制."

如果您需要的是"某些逻辑,绘制,更多逻辑",那么您需要将"更多逻辑"放在一个单独的方法中并使用-performSelector:withObject:afterDelay:延迟为0 来调用它.这将在"更多逻辑"之后下一个绘制周期.请参阅此问题以获取此类代码的示例,以及可能需要它的情况(尽管通常最好寻找其他解决方案,如果可能,因为它会使代码复杂化).

如果你不认为事情正在被绘制,请设置一个断点-drawRect:并查看你何时被召唤.如果您正在打电话-setNeedsDisplay,但未-drawRect:在下一个事件循环中调用,那么请深入了解您的视图层次结构并确保您不会在某个地方尝试智取.在我的经历中,过于聪明是导致糟糕绘画的首要原因.当你认为你最了解如何欺骗系统做你想做的事情时,你通常会把它做成你不想要的.


Wer*_*her 51

调用setNeedsDisplay和drawRect之间有一个很大的延迟问题:(5秒).事实证明我在与主线程不同的线程中调用了setNeedsDisplay.将此调用移至主线程后,延迟消失.

希望这个对你有帮助.


Sli*_*son 14

强制视图同步绘制 (返回调用代码之前)的退款保证,强化 - 具体 - 固体方式是配置CALayerUIView子类的交互.

在你的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];
}
Run Code Online (Sandbox Code Playgroud)

还实现一个- 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();
}
Run Code Online (Sandbox Code Playgroud)

并创建自定义- 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];
}
Run Code Online (Sandbox Code Playgroud)

而现在[myView display]只要你真的需要它就可以打电话. - display将告诉CALayerto displayIfNeeded,它将同步回调我们- drawLayer:inContext:并进行绘图- internalDrawWithRect:,在继续之前更新视觉与绘制到上下文中的内容.


这种方法类似于上面的@ RobNapier,但- displayIfNeeded除此之外还具有调用的优点- setNeedsDisplay,这使得它同步.

这是可能的,因为CALayers暴露了比UIViews更低的绘图功能- 层比视图更低层,并且为了在布局中高度可配置的绘图而明确设计,并且(像Cocoa中的许多东西一样)被设计为灵活使用(作为父类,或作为委托人,或作为其他绘图系统的桥梁,或仅仅是他们自己的).

有关CALayers可配置性的更多信息,请参阅"核心动画编程指南"的" 设置图层对象"部分.


dre*_*zor 5

我遇到了同样的问题,SO或Google的所有解决方案都不适用于我.通常,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];
});
Run Code Online (Sandbox Code Playgroud)

如果您不需要经常重绘视图,这是一个很好的解决方案.否则,如果你正在做一些移动(动作)的东西,通常没有问题只是打电话setNeedsDisplay.

我希望它会帮助那些迷失在那里的人,就像我一样.