如何正确使用自定义视图?

时间:2011-04-12 22:38:44

标签: objective-c cocoa nsview

我一直在尝试制作一个简单的绘图程序。最近,我已经想出为此目的在自定义视图中绘制形状。我的问题是我必须在一个时间点绘制所有内容。我不知道这是否真的有意义,但在我看来它只调用drawRect方法一次,启动时“一次”。

到目前为止,这是我的代码:

头文件。

NSBezierPath *thePath;
NSColor *theColor;
NSTimer *updateTimer;
NSPoint *mousePoint;
int x = 0;
int y = 0;

@interface test : NSView {
    IBOutlet NSView *myView;

}

@property (readwrite) NSPoint mousePoint;

@end

然后,在.m文件中实现。

@implementation test

@synthesize mousePoint;

- (void) mouseDown:(NSEvent*)someEvent {         
    CGEventRef ourEvent = CGEventCreate(NULL);
    mousePoint = CGEventGetLocation(ourEvent);
    NSLog(@"Location: x= %f, y = %f", (float)mousePoint.x, (float)mousePoint.y);
    thePath = [NSBezierPath bezierPathWithRect:NSMakeRect(mousePoint.x, mousePoint.y, 10, 10)];
    theColor = [NSColor blackColor];  

} 

- (void) mouseDragged:(NSEvent *)someEvent {
    mousePoint = [someEvent locationInWindow];
    NSLog(@"Location: x= %f, y = %f", (float)mousePoint.x, (float)mousePoint.y);
    x = mousePoint.x;
    y = mousePoint.y;
    [myView setNeedsDisplay:YES];

}

- (void) drawRect:(NSRect)rect; {
    NSLog(@"oisudfghio");
    thePath = [NSBezierPath bezierPathWithRect:NSMakeRect(x, y, 10, 10)];
    theColor = [NSColor blackColor];
    [theColor set];
    [thePath fill];

}

@end

启动时,它会在左下角绘制一个矩形,就像它应该的那样。问题是,drawRect方法仅在启动时调用。无论我做什么,它都不会开火。

编辑:我刚刚更新了代码。我希望它有所帮助。

第二次编辑:我真的简化了代码。我希望这会有所帮助。

2 个答案:

答案 0 :(得分:6)

简答: 当您的视图状态发生更改以使其绘制方式不同时,您需要调用 - [NSView setNeedsDisplay:]。这将导致您的视图的drawRect:方法在不久的将来被调用。你永远不应该打电话给drawRect:你自己。这是一个代表你调用的回调。

当您的应用程序中发生导致您想要更改绘图的事件时,请捕获实例变量中发生的事件的状态,调用setNeedsDisplay:然后在调用drawRect:时执行新绘图。

长答案: 在Cocoa中,窗口绘制是使用拉/无效模型完成的。这意味着窗口知道它是否需要绘制,当它认为需要绘制时,它会在每个事件循环中绘制一次。

如果您不熟悉事件循环,可以在Wikipedia

上阅读

在应用程序的顶层,您可以想象Cocoa正在这样做:

while (1) {
   NSArray *events = [self waitForEvents];
   [self doEvents:events];
}

事件是鼠标移动,键盘按下,定时器关闭等事件。

NSView有一个方法 - [NSView setNeedsDisplay:]。它需要一个布尔参数。当调用该方法时,窗口使该视图的绘图区域无效,并安排一个事件以供将来重绘 - 但前提是没有预先安排的重绘事件。

当runloop下次旋转时,将重新绘制使用setNeedsDisplay:标记的视图。这意味着您可以连续多次调用setNeedsDisplay:并且绘图将被批量调用到将来的一次drawRect调用:这对于性能原因很重要,这意味着您可以在一个方法中多次更改视图的框架,但只能在最终位置绘制一次。

答案 1 :(得分:1)

示例中的代码存在一些问题。第一个是所有绘图代码必须在drawRect:方法或从drawRect:调用的方法中,因此您在其他方法中放置的绘图代码在运行时将不起作用。第二个问题是您的代码永远不应该直接调用drawRect:;相反,框架将在每个事件周期自动调用它(如果需要)。

不考虑对所有值进行硬编码,而是考虑将实例变量用于您希望能够在运行时更改的内容,例如绘图颜色和矩形。然后在mouseDragged:方法中,发送myView消息中的视图(setNeedsDisplay:)。如果您传递YES作为参数,框架将为您调用drawRect:方法。