[iOS 앱 만들기 002] 윈도우 객체

iOS앱에서 무슨 윈도타령인가? 하지만 실제로 그렇다. iOS앱은 한 번에 한 개 앱이 화면을 꽉 채운 상태로 실행된다. 데스크톱용 앱과는 달리 화면에는 항상 1개의 앱만 표시되기 때문에 윈도의 테두리나 제목막대 같은 게 표시되지 않고 “스크린 위에 뷰가 있는 형태”가 되는데…. 스크린에 표시되는 뷰의 계층 구조에서 최상위 뷰의 역할을 할 고정된 객체가 필요한데, 이 역할을 윈도 객체가 한다. 따라서 윈도 객체(UIWindow)는 UIView의 서브 클래스이며, 주로 앱 델리게이트의 프로퍼티로 접근하게 된다.

윈도 객체의 생성

메인 nib 파일을 사용하는 경우라면, window 객체를 nib 파일안에 만들어 두고 앱 델리게이트의 프로퍼티로 연결시켜놓기만 하면 된다. 만약 nib 파일을 사용하지 않거나, 사용하더라도 윈도 객체를 만들지 않았다면 코드 상에서 생성해야 하는데 화면을 표시하기 직전인 application:didFinishLaunchingWithOptions: 메소드를 오버라이드 하면서 이 곳에서 처리를 하게 된다. 또한 생성된 윈도객체는 화면에 표시되기 위해서 makeKeyAndVisible 메소드를 호출해 주어야 한다.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    {
        _window = [[UIWindow alloc] initWithFrame:[UISCreen mainScreen] bounds];
        [_window setBackgroundColor:[UIColor whiteColor]];
        [_window makeKeyAndVisible];

        return YES;
    }

뷰 추가하기

위 코드는 비어있는 흰색 윈도를 만들고 이를 화면에 표시한다. 만약 실행된다면 아무것도 없는 흰 바탕의 앱 화면이 표시될 것이다. 만약 첫화면을 구성하는 메인 뷰 객체를 가지고 있다면, 화면에 표시하기 전에 윈도 객체의 서브 뷰 처럼 추가해주면 된다.

    [_window addSubView:self.rootView];

루트 뷰 컨트롤러

윈도는 뷰 계층구조(View Hierarchy)에서 최상위의 고정된 뷰이다. 따라서 실제 UI를 구성하는 View 들은 이 윈도의 subview가 되거나 혹은 subview의 subview가 된다. 그리고 각각의 뷰를 제어하는 컨트롤러는 가능한 별도로 가져가는 것이 좋다. (앱 델리게이트의 덩치를 키우지 않는 게 좋다) 뭐 그 이유에 대해서는 찾아보면 많이 나올테니 굳이 말할 필요가 없을 것 같고… 어쨌든 보통은 루트 뷰는 뷰 컨트롤러를 통해서 사용자 액션에 대해 반응하거나 UI를 업데이트 하도록 하는 것이 보편적이다. 그래서 다음과 같은 코드가 종종 발견된다.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    {
        _window = [[UIWindow alloc] initWithFrame:[UISCreen mainScreen] bounds];
        [_window setBackgroundColor:[UIColor whiteColor]];
        _rootViewController = [MYRootViewController alloc] init];
        [_window addSubview:_rootViewController.view];
        [_window makeKeyAndVisible];

        return YES;
    }

루트 뷰의 컨트롤러를 만들고 그 컨트롤러의 뷰를 윈도에 추가하는 형태를 사용했다.

뷰 컨트롤러의 뷰 속성에 접근하면, 뷰 컨트롤러는 자동으로 loadView메소드를 호출하여 view를 생성한다. 스토리보드를 사용한다면 스토리보드로부터 뷰를 로드하고, 만약 아무런 커스터마이징이 없다면 비어있는 뷰 객체를 얻게 된다.

하지만 다음과 같이 쓰는 것이 좀 더 깔끔하다.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    {
        _window = [[UIWindow alloc] initWithFrame:[UISCreen mainScreen] bounds];
        [_window setBackgroundColor:[UIColor whiteColor]];
        MYRootViewController *_rootViewController = [[MYRootViewController alloc] init];
        [_window setRootViewController:_rootViewController];
        [_rootViewController release];
        [_window makeKeyAndVisible];

        return YES;
    }

윈도 객체는 rootViewController라는 속성을 가지고 있다. 이 속성에 대해 뷰 컨트롤러 객체를 세팅하면 (이것을 루트 뷰 컨트롤러를 인스톨한다고 표현한다.) UIWindow 객체는 알아서 해당 뷰 컨트롤러의 뷰를 자신의 서브뷰로 등록하게 된다. 따라서 별도의 뷰를 추가하는 코드 없이 자동으로 뷰가 화면에 표시된다. 또한 이러한 방식의 코드는 루트 뷰 컨트롤러가 ‘컨테이너 뷰 컨트롤러’ 타입일때도 별다른 코드의 변경없이 그대로 적용이 가능하다는 장점이 있다.

자, 여기까지해서 iOS 앱 구동에 필수적인 앱 델리게이트와 윈도 객체 생성 (그리고 메인 뷰를 만들어서 화면에 표시하는 과정까지)을 살펴보았다.

다음 시간부터는 뷰와 뷰 컨트롤러에 대해 살펴보도록 하겠다. 별거 아니면 별거 아닐수도 있지만 양은 많고 필력은 딸리니 그게 걱정이다.