[App 개발] 질문하나 있습니다.
본문
다름이 아니라, Xcode에서 Interface Builder로 Custom View를 하나 만들었고, 이 클래스로 파일을 생성(.h, .m파일)하여 Xcode에서 .m파일에 이 Custom View에 원과 선을 그리는 메소드를 하나 생성했습니다.
여기까지가 제가 책을 따라한 내용이구요.
제가 아무리 둘러봐도 이 클래스파일의 원과 선을 그리는 메소드를 활성화시키는 구문을 찾아보질 못했는데, 프로그램을 실행시키면 원과 선이 Custom View에 그려지더군요.
제가 가진 짧은 프로그래밍지식으로는 좀 이해가 안가거든요.
소스는 아래와 같습니다.
#import "DotView.h"
@implementation DotView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
center.x = 50.0;
center.y = 50.0;
radius = 20.0;
color = [[NSColor redColor] retain];
return self;
}
- (void)awakeFromNib
{
[colorWell setColor:color];
[slider setFloatValue:radius];
}
- (void)dealloc
{
[color release];
[super dealloc];
}
- (void)drawRect:(NSRect)rect
{
NSRect dotRect;
[[NSColor whiteColor] set];
NSRectFill([self bounds]);
dotRect.origin.x = center.x - radius;
dotRect.origin.y = center.y - radius;
dotRect.size.width = 2 * radius;
dotRect.size.height = 2 * radius;
[color set];
[[NSBezierPath bezierPathWithOvalInRect:dotRect] fill];
}
- (BOOL)isOpaque
{
return YES;
}
- (void)mouseDown:(NSEvent *)event
{
NSPoint eventLocation = [event locationInWindow];
center = [self convertPoint:eventLocation fromView:nil];
[self setNeedsDisplay:YES];
}
- (IBAction)setColor:(id)sender
{
NSColor * newColor = [sender color];
[newColor retain];
[color release];
color = newColor;
[self setNeedsDisplay:YES];
}
- (IBAction)setRadius:(id)sender
{
radius = [sender floatValue];
[self setNeedsDisplay:YES];
}
@end
drawRect 메소드가 그려주는 메소드인데
NSBezierPath 함수를 이용해 그려주는 구문이 있지만, 이 메소드를 불러내는 구문이 있어야 이 메소드가
활성화되어서 그림을 그리는 것이 아닌가요?
그냥 선언만해주면 자동으로 그리는 건가요? 이해가 안가거든요.
이해를 좀 시켜주세요.
답변 부탁드릴께요.
최신글이 없습니다.
최신글이 없습니다.
댓글목록 6
hongjuny님의 댓글
안녕하세요. ^^
Object oriented 프로그래밍 모델을 가지고 있는 거의 대부분의 소프트웨어들이 유사한 구조를 가지고 있는데요, 일종의 callback pattern 이라고 할 수 있겠습니다.
drawRect 메쏘드는 개별 프로그래밍에서 호출되는 경우는 없습니다. (할 수 있더라도 하지 말아야 합니다. 이유는 나중에...) 그 대신 이 메쏘드는 중앙 시스템, 주로 윈도우 매니저에서 호출하게 됩니다.
그 이유는 다음과 같습니다. 예를 들어, 두 개의 윈도우가 열려 있습니다. 그 중에 하나는 다른 윈도우에 반쯤 가리워져 있다고 해 봅시다. 그런데 뒤에 가리워져 있는 윈도우가 자기 맘대로 리소스를 점령하여 그림을 그린다면, 윈도우 화면이 망가지게 되지요. (물론 이것은 이해를 돕기 위한 설명입니다. ^^)
그래픽 디바이스의 리소스 관리, 윈도우 정렬, 화면 갱신 등의 작업은 윈도우 매니저를 통해서 이루어지고, 매니저에서는 개별 view 클래스가 움직이거나 크기가 바뀌었을 때, 윈도우 내용이 갱신될 필요가 있을 때 그 클래스의 drawRect 를 호출하게 되는 것이죠. Java에서는 paint() 메쏘드가 비슷한 일을 하고 있고, Win32 에서는 WM_PAINT 메세지가 그런 역할을 담당하고 있습니다.
객체 지향 시스템의 프로그래밍은 마치 '빈 칸 채워넣기'같은 느낌을 줄 때가 있습니다. 화면을 갱신할 때, 마우스 입력이 들어왔을 때, 타이머 이벤트가 발생했을 때... 이런 식으로 전달된 메세지들을 처리해 주는 루틴을 채워넣는 일이 많기 때문이죠. 그래서 마치 선언만 되어 있으면 자동으로 그려지는 것 같이 느껴지는 것이 아닌가 싶습니다.
Realmac님의 댓글
hongjuny님 답변 감사드려요. 근데 다시 의문이 일어나거든요.
일단 제가 답변을 읽고 이해하기는 DotView라는 것이 윈도우의 한 View클래스이므로 그 안에 있는 메소드도 윈도우매니져라는 것에 의해 자동으로 실행되는 것이라고 이해가 됐는데 맞는지 모르겠습니다.
그런데 그렇다면, 만약 리얼베이직(리얼베이직을 접해보셨는지는 모르겠지만)의 캔버스 컨트롤처럼 백지상태의 View에서 사용자가 임으로 원이나 선, 등의 도형을 임의로 그리고 다시 지울수있는 View를 Cocoa에서 만들려고 한다면 그려주고 지워주는 메소드를 임의로 필요할때만 호출하는 식으로 해야 가능할텐데요 . 그럴 경우엔 어떤 식으로 해야하는지요. 그냥 윈도우매니져에서 자동으로 메소드를 실행하는식으로는 안될 것 같은데요.
hongjuny님의 댓글
네 맞습니다. ^^ 그리고, 콜백 모델에서 리얼 베이직처럼 어떤 그림 출력을 순차적으로 그려주는 루틴을 만드는 것은 전혀 다른 접근방식을 요구하게 됩니다. 시퀀셜 모델에서는 정해진 과정을 순차적으로 처리하고 프로그램을 종료하면 끝이지만, 콜백 모델에서는 객체의 생성, 처리, 소멸 등의 과정을 기술해주어야 하니까요.
요점은 이렇습니다. 프로그램의 구조가 전혀 다르기 때문에 접근하는 방법이 달라지게 됩니다.
리얼 베이직 타입의 그림 그리기 루틴을 콜백 모델에서 구현하려면 먼저 객체 내에 그림 그리기용 개인 메쏘드를 하나 선언합니다.그리고 그 메쏘드 내에서 출력 리소스를 할당한 다음 그림 그리기 루틴을 짜 주는 것이죠. 그 다음에는 만들어진 개인 메쏘드를 호출할 수 있는 방법을 마련해야 합니다. 예를 들어, 쉬운 방법으로는 "Run" 메뉴 항목을 하나 만들어서 사용자가 그 메뉴를 선택하면 곧바로 만들어둔 개인 메쏘드를 호출하는 방식으로 말이지요.
모델마다 장단점은 있습니다. 시퀀셜한 프로그램은 직관적이고 간단해서 프로그래밍이 편하지만, 규모가 큰 범용 소프트웨어를 구현하는 데에는 콜백 모델이 여러모로 유리합니다.
Realmac님의 댓글
답변 또 감사드리구요. ^^.
제 짧은 지식으로 이해하기가 벅차네요.
그러니까, 만약 위와 같은 경우를 만들려면, View클래스가 아닌 다른 객체에 그림을 그려주는 메소드를 선언한다음 이 메소드를 메뉴항목같은 곳에서 호출해서 원하는 View에 출력하는 방식으로 그려야한다구요? 제대로 이해한건지 모르겠네요.
그런데 이전 답변에 DrawRect같은 메소드가 개별프로그램에서 호출되는 경우는 없다고 하셨는데 DrawRect같은 메소드말고 다른 그림을 그려주는 메소드를 선언해야하는건지요? 어떻게 다른건지요.?
hongjuny님의 댓글
일단 정리를 해 보겠습니다. ^^
1. 먼저, 그림 그리기 프로그래밍 연습을 위한 예제는 위에서와 같이 drawRect 메쏘드 내에 프로그램을 만들어 넣으셔도 됩니다. 어차피 윈도우가 열리면서 최초 한 번은 윈도우 매니저에서 drawRect 메쏘드를 호출할 것이기 때문에 당연히 그 루틴은 실행됩니다. 전혀 문제 없습니다.
2. drawRect 메쏘드의 원래 취지는 "뷰 객체의 현재 상태를 표시하는" 역할입니다. 예를 들어 iTunes 의 메인 뷰 객체가 있습니다. 잠시 사파리를 열어서 웹서핑을 하다가 사파리 창을 닫았습니다. 이 때 윈도우 매니저는 뒤에 가리워져 있던 iTunes 에게 "사파리 때문에 가리워져 있던 이 부분에서부터 이 부분까지의 내용을 새로 그려 넣어라" 라는 명령을 내려보닙니다. 이 명령이 바로 drawRect 메쏘드입니다. drawRect 의 argument 로 NSRect 가 하나 전달되는 것이 보이시죠? 책에서는 이것이 화면 clipping 영역이라고 되어 있을 것입니다. 바로 사파리 때문에 지워졌던 그 영역을 가리키는 것이죠.
3. 따라서 drawRect 명령을 받은 iTunes 는 자신의 몸체를 다 그려줘야 합니다. 메탈 프레임부터 시작해서 버튼, 곡 항목들... 버튼이라든지 곡 리스트 들은 모두 iTunes 의 메인 뷰에 속해있는 child view 들입니다. 모든 뷰에 drawRect 가 호출되어야겠지요? 그렇게 해서 전체 화면을 복원합니다.
4. 그러나 자잘한 화면 갱신, 예를 들어서 마우스 버튼을 눌렀을 때 어떤 특정 화면에 글씨가 나타나게 한다든지, 버튼 모양을 눌린 모습으로 그려줬다가 마우스를 떼면 원래대로 바꾼다든지 하는 작업들은 drawRect 를 통해서 할 필요가 없습니다. 그 때는 drawRect 처럼 화면 전체를 그려주는 루틴이 아니라, 필요한 특정 작업만을 지정하는 루틴이 필요하게 되죠.
정리가 잘 되었나 모르겠습니다. ^^
Realmac님의 댓글
네~ㅅ 답변감사해요. 어느정도 이해가 된 듯 하네요.
그리고 또 다른 질문들 올릴텐데 답변 부탁드릴께요. 꾸벅~ ^^