我一直在尝试制作一个简单的绘图程序。最近,我已经想出为此目的在自定义视图中绘制形状。我的问题是我必须在一个时间点绘制所有内容。我不知道这是否真的有意义,但在我看来它只调用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
方法仅在启动时调用。无论我做什么,它都不会开火。
编辑:我刚刚更新了代码。我希望它有所帮助。
第二次编辑:我真的简化了代码。我希望这会有所帮助。
答案 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:
方法。