Как правильно использовать пользовательский вид?
Я пытался сделать простую программу для рисования. Недавно я решил нарисовать фигуры в специальном представлении для этой цели. Моя проблема в том, что я должен нарисовать все в один момент времени. Я не знаю, имеет ли это смысл, но мне кажется, что это 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];
}
@конец
При запуске он рисует прямоугольник в левом нижнем углу, как и должно быть. Проблема в том, drawRect
Метод вызывается только при запуске. Это просто не сработает, что бы я ни делал.
РЕДАКТИРОВАТЬ: я только что обновил код. Я надеюсь, что это помогает.
ВТОРОЕ РЕДАКТИРОВАНИЕ: Я действительно упростил код. Я надеюсь, что это поможет немного больше.
2 ответа
Краткий ответ: Когда состояние вашего представления изменяется так, что оно будет отображаться по-другому, вам нужно вызвать -[NSView setNeedsDisplay:]. Это приведет к вызову drawRect: метод вашего представления в ближайшем будущем. Вы никогда не должны вызывать drawRect: себя. Это обратный вызов, который вызывается от вашего имени.
Когда в вашем приложении происходят события, которые заставляют вас захотеть изменить свой чертеж, запишите состояние того, что произошло, в переменные экземпляра, вызовите setNeedsDisplay: и затем позже, когда вызывается drawRect:, создайте новый чертеж.
Длинный ответ: В Какао рисование окон выполняется с помощью модели извлечения / аннулирования. Это означает, что у окна есть представление о том, нужно ли ему рисовать, и когда оно думает, что ему нужно рисовать, оно рисует один раз за цикл событий.
Если вы не знакомы с циклами событий, вы можете прочитать о них в Википедии
На верхнем уровне приложения вы можете представить, что Cocoa делает это:
while (1) {
NSArray *events = [self waitForEvents];
[self doEvents:events];
}
Где события - это такие вещи, как движение мыши, нажатие клавиатуры и отключение таймеров.
NSView имеет метод -[NSView setNeedsDisplay:]. Он принимает логический параметр. Когда этот метод вызывается, окно делает недействительной область рисования для этого вида и планирует событие на будущее, чтобы сделать перерисовку - но только если не запланировано ранее существующее событие перерисовки.
Когда runloop вращается в следующий раз, представления, помеченные setNeedsDisplay: перерисовываются. Это означает, что вы можете вызывать setNeedsDisplay: несколько раз подряд и рисование будет объединено в один вызов drawRect: в будущем. Это важно с точки зрения производительности и означает, что вы можете делать такие вещи, как изменение фрейма вида несколько раз в одном методе, но он будет нарисован только один раз в конечном местоположении.
Код в вашем примере имеет несколько проблем. Во-первых, весь код рисования должен быть в drawRect:
метод или метод, вызываемый из drawRect:
таким образом, код рисования, который вы поместили в другие ваши методы, не будет иметь никакого эффекта во время выполнения. Вторая проблема заключается в том, что ваш код никогда не должен вызывать напрямую drawRect:
; вместо этого платформа будет вызывать его автоматически (при необходимости) один раз за цикл события.
Вместо того, чтобы жестко кодировать все значения, рассмотрите возможность использования переменных экземпляра для вещей, которые вы хотите изменить во время выполнения, например, для цвета чертежа и прямоугольника. Тогда в вашем mouseDragged:
метод, отправьте представление (myView
в вашем примере) setNeedsDisplay:
сообщение. Если вы пройдете YES
в качестве аргумента drawRect:
метод будет вызываться для вас фреймворком.