在BubbleLevel的iPhone上重画UIControls/UIButtons示例代码

问题描述:

我很困惑-setNeedsDisplay何时应该被我调用。作为一个例子,为了更新按钮之间不同图像的按钮(在两者之间切换),苹果的示例代码显示了在UIButton上调用的-setImage。然而,在那之后我没有找到-setNeedsDisplay的调用。所以UIControl方法都在内部执行-setNeedsDisplay,因此-setNeedsDisplay仅在操作UIView实例时使用?还是有更具体的语义?如果我继承UIView,我的方法是否应该总是在内部处理重绘?这是在文档中指定的方式/位置?在BubbleLevel的iPhone上重画UIControls/UIButtons示例代码

LevelView.m在BubbleLevel源码:

- (void)toggleHoldButton:(id)sender { 
    if (holdButtonIsShowing == YES) 
    { 
     holdButtonIsShowing = NO; 
     [holdButton setImage:[UIImage imageNamed:@"release_button.png"] forState:UIControlStateNormal]; 
    } else 
    { 
     holdButtonIsShowing = YES; 
     [holdButton setImage:[UIImage imageNamed:@"hold_button.png"] forState:UIControlStateNormal]; 
    }  
} 

setNeedsDisplay用于重新呈现或重新绘制图(UIView的或UIView子类实例)。从我们的角度来看:它调用drawRect:但实际上在它之前它准备了上下文并使视图准备好重新绘制。 (这就是为什么你不应该直接调用drawRect的原因)

一个常见的情况是当你实现了drawRect:方法(一些例子:在视图中绘制内容或使用CoreText绘制文本或使用CoreGraphics绘制一些图形等) 如果你想在某个特定的时刻更新绘图,你可以调用setNeedsDisplay,它会调用drawRect:并且你的视图将被重绘。如果不是这样,当视图的边界发生改变时,drawRect:方法将被调用,而在其他情况下,系统决定有必要重新绘制视图。

在一个按钮的情况下,你不需要这样做,因为当按钮被按下时,setNeedsDisplay的内部调用将被完成,使得按钮改变等等(谁真的知道发生了什么背景:你不必担心drawRect:以这种方式使用UIButtons)

你也可以通过移动它的子图层或子视图来改变视图的外观,这并不一定意味着重绘视图。当您查看容器而不是绘图对象时,就是这种情况。

一个很好的规则可能是,如果您没有实现drawRect:方法,则不需要显式调用setNeedsDisplay。

http://developer.apple.com/iphone/library/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/drawRect

希望它能帮助。

;)

UPDATE:2009/09/09

的UIButton调用的drawRect方式:取决于它的实施和我们应该不会去关心它(至少不,如果我们不继承) 。从另一个对象

- (void) drawRect:(CGRect)frame{ 
    NSLog(@"%s is being called!!!", _cmd); 
    [super drawRect:frame]; 
} 

现在尝试加载它,调整其大小,移动它,滚动它(如果可用),隐藏它,看看调试控制台(:但如果你真的了解它,你可以继承它,并覆盖Cmd + Shift + R) 我建议不仅为UIButton,而且为普通的UIView。 当drawRect:被调用时,这肯定会给出一个清晰的概念。 ;)(其他方法layoutSubviews:等)

我说我们不应该关心drawRect的原因:在UIButton是因为我们不需要它,我们使用setImage为某个状态预设图像:forState:就是这样。我们可以随时重新设置这些图像,并且按钮会根据需要更改其图像,并且可能会根据苹果工程师设计它的方式重新绘制其视图。

关于你的问题,如果setNeedsDisplay符合我给你的规则。我相信是的。 我在每个方法的开头都放了一个断点,并且没有任何东西,并且检查了setNeedsDisplay被调用时发生了什么。没有任何LeverView的方法被调用! 然后我评论了setNeedsDisplay的3个调用,结果是一样的! (iOS4.1) 因此这三行代码是不需要的。 ;)

调用setNeedsDisplay时必须小心,因为根据您的实现它可能会使您的程序非常慢!因为会重绘一切,可能不是必需的。 ;)

Pheeew ...大声笑。相当长的帖子只关于drawRect:

干杯

+0

非常感谢。尽管如此,如果我想尝试重新创建UIButton类,我仍然很好奇drawRect:何时发生。它是1)目标行动被调用(其中按钮元素可以通过例如-setImage :)被告知更新,然后控制返回给调用者,它执行一个setNeedsDisplay?或2)每个消息,如setImage:发送到UIButton对象本身涉及重新显示?同样,对于这个经验法则,它是否包含在setupSubviewsWithContentFrame内相同示例中对setNeedsDisplay的调用:? – snackdefend 2010-09-08 16:58:31

+0

我更新了我的帖子;)请检查它。 – nacho4d 2010-09-09 06:37:37

+0

非常感谢,这个子类化的想法非常棒,现在就尝试。我很欣赏setNeedsDisplay澄清;在对它们进行评论之后,我也得到了相同的结果。我假设LevelView的init中的调用只是希望调度程序在应用程序启动后尽快开始绘制。 – snackdefend 2010-09-14 02:21:20