- 앱의 보이는 콘텐츠가 포함된다.
- View 및 기타 앱 객체에 터치 이벤트를 전달하는 데 중요한 역할을 한다.
- ViewController와 함께 방향 변경을 처리한다.
- 대부분의 경우, 앱의 Window가 이러한 책임들을 충족시킬 수 있도록하기 위해 따로 조치를 취할 필요는 없다.
- 스토리 보드를 사용하여 앱의 사용자 인터페이스를 만드는 경우, 외부 디스플레이를 지원할 때에나 Window 객체를 명시적으로 만든다.
- Window에는 다른 모든 view를 포함하는 single root view 개체가 있다.
따라서 이 root view에 addSubvew를 하는 방법도 있으나, rootViewController를 제공하는 것이 window의 contents들을 관리하는데 더 효과적이다.
( -> window 객체와 contents 사이의 구분이 유지되기 때문) (앱이 background로 진입시 민감한 데이터를 빠르게 감추기 위해 사용할 수도 있다. Bacground로 진입시 UIWindow를 생성하여 스크린을 덮으면 됨.)
- 앱 실행시에 window를 만들고, 이를 유지하고, app delegate 객체에 참조를 저장해야 한다.
- 추가 window를 만드는 경우, 필요시에 지연생성(lazily) 해야 한다.(외부 디스플레이에 내용을 표시할 수 있으면, 해당 window가 만들어지기 전에 디스플레이가 연결될 때 까지 기다려야 한다.)
- UIWindow를 생성할 때는 항상 window의 초기 size와, 해당 window가 보여질 screen을 지정해야 하며, 보여질 contents를 제공할 rootViewController를 지정해야 한다.
- Window의 root view에는 원하는 view를 사용할 수 있는데, 이 root view를 구성할 때 초기 크기 및 위치를 지정해줘야 한다. (Root view를 TabbarController, Navigation Controller, Split-View Controller와 같이 container viewController에 의해 제공되는 경우는 초기 크기를 설정할 필요 없이 자동으로 조절된다.)
- 만약 custom window를 제공하고 싶다면 AppDelegate 내의 UIWindow: window 라는 property의 getter method를 구현해야 하고, custom window를 return 해야 한다.
- AppDelegate 내의
application:didFinishLaunchingWithOptions:
메서드 내에서 window를 생성하고makeKeyAndVisible
을 호출한다.makeKeyAndVisible
은 해당 window를 보여주고 다른 모든 window 앞에 배치하기 위한 메서드이다.
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
_window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init];
rootViewController.view.backgroundColor = UIColor.greenColor;
[_window setRootViewController:rootViewController];
[_window makeKeyAndVisible];
return YES;
}
- RunTime시에 window에 접근하기 위해선, UIApplication의 delegate 또는 nib 파일의 File’s Owner에 정의된 outlet에 해당 window를 연결해야 한다.
- Nib 파일로부터 window의 rootViewController 속성을 구성할 수 있는데, 이 속성은 window의 root view를 구성한다.
- Window가 로드될 때 이 속성이 설정되면 UIKit은 연결된 viewcontroller의 view를 window의 root view로 자동 설정한다.
- 또한 Info.plist 내의 NSMainNibFile 키를 nib 파일의 이름으로 설정해야 한다.
- Interface builder에서 window를 생성할 때, attributes inspector에서 launch option을 full screen으로 하는 것이 좋다. 이 옵션을 사용하지 않은 상태에서 window의 크기가 device의 화면보다 작으면 일부 view에서 터치 이벤트가 수신되지 않는다. 이는 window가 자신의 bounds 바깥의 터치이벤트는 받지 않기 때문이다.
- XCode의 기본 프로젝트 템플릿에서는 기본적으로 main window를 생성하며, AppDelegate 객체 내의 window property를 정의한다.
앱의 기본 스토리 보드 파일을 만들고 Info.plist의 기본 스토리 보드로 설정하면 iOS에서 몇 가지 설정 작업을 수행한다.- window를 인스턴스화 한다.
- 메인 스토리보드를 로드하고 initial ViewController를 인스턴스화 한다.
- 새 ViewController를 window의 rootViewController속성에 할당한 다음 window를 표시한다.
- 초기 ViewController가 표시되기 전에 ViewController를 구성할 수 있도록 AppDelegate가 호출된다.
- UIWindow 객체에는 해당 window가 다른 window를 기준으로 배치되는 방법을 결정하는 windowLevel 속성이 있다. Window가 다른 window에 대한 상대적인 z축 위치를 나타낸다.
@property(nonatomic) UIWindowLevel windowLevel; // default = 0.0
- 새 window는 생성시
UIWindowLevelNormal
로 자동 지정된다.UIWindowLevelNormal
은 앱 관련 콘텐츠를 표시하는 window가 갖는다. 상위 레벨로는UIWindowLevelAlert
와UIWindowLevelStatusBar
가 있는데, 상태표시줄이나 경고메세지와 같이 application 내용 위로 떠야 하는 정보를 위해 예약되어있다. (물론 이 레벨로 윈도우를 할당할 수 있지만 일반적으로는 시스템이 이 작업을 수행한다.)
-
Application이 window를 표시하거나 숨길 때, 그에 따라
UIWindowDidBecomeVisibleNotification
및UIWindowDidBecomeHiddenNotification
notification이 전달된다. 이는 application이 background로 전환될때는 전달되지 않는다 (applicataion의 context 내에서 보이는 것으로 간주 하기 때문) -
UIWindowDidBecomeKeyNotification
,UIWindowDidResignKeyNotification
이 두 notification의 역할은, application이 어떤 window가 key window인지 추적할 수 있게 해준다. 즉, 현재 키보드 이벤트 및 기타 비터치 관련 이벤트(좌표값이 없는 이벤트)를 받고 있는지 추적이 가능하다. 좌표값이 있는 터치 이벤트는 해당 터치가 발생한 window로 전달되는 반면, 좌표값이 없는 터치 이벤트는 application의 key window로 전달된다. -
Custom Window를 만들 필요는 거의 없으나, 만들게 된다면 가장 대표적인 이유는
becomeKey()
orresignKey()
메서드를 override하여 window의 key 상태가 변경될 때 사용자 정의 behavior를 구현하는 것이다. -
key window
- 가장 최근에
makeKeyAndVisible()
메세지를 받은 window. - 키보드 및 비터치(좌표 값이 없는) 이벤트를 받는 window.
- 한 번에 하나의 window만 key window가 될 수 있다.
- 가장 최근에
-
UIWindow 클래스의 screen 속성(UIScreen의 인스턴스)는 window가 현재 표시되어있는 특정 Device의 디스플레이를 나타낸다. 이 property에는 screen의 bounds, mode, 밝기와 같은 device 디스플레이에 대한 정보가 포함된다.
-
이 UIScreen 객체는 device의 디스플레이의 변경사항을 모니터링 할 수 있는 여러가지 noti들이 포함되어 있다. (디스플레이 연결 or 연결끊김, 디스플레이의 모드 or 밝기값 변경)
-
UIScreen 클래스는 사용 가능한 하드웨어 디스플레이를 나타내는 screen 객체 목록을 유지 관리한다.
-
일반적으로 모든 iOS Device의 main display를 나타내는 screen 개체는 하나이지만, 외부 디스플레이 연결을 지원하는 Device는 추가 screen 개체를 사용할 수 있다
-
외부 디스플레이에 컨텐츠를 표시하는 프로세스는 대략적으로 다음과 같다
- Application 시작시 Screen 연결 및 연결 해제 noti 등록.
- 외부 디스플레이에 내용을 표시할 시간이 되었을 때 Window를 작성하고 구성.
- UIScreen의 screen property를 사용하여 외부 디스플레이에 대한 screen 개체를 가져온다.
- UIWindow 객체를 만들고 screen에 맞게 크기 조정한다.
- 외부 디스플레이에 대한 UIScreen 객체를, window의 screen property에 지정한다.
- 컨텐츠에 맞게 screen의 해상도를 조정한다.
- 적당한 views를 window에 추가한다.
- Window를 표시하고 update한다.
-
사용자가 디스플레이를 연결하거나 연결을 끊으면 시스템에서 응용 프로그램에 알맞은 알림을 전송하는데, 이러한 알림을 사용하여 응용 프로그램 상태를 업데이트하고 외부 디스플레이와 연결된 창을 만들거나 해제해야 한다.
-
연결 및 연결 해제 noti는 application이 background에서 일시 중지 된 경우에도 언제든지 올 수 있다. 따라서 application delegate와 같이 application의 런타임동안 존재하게 될 개체로부터 noti를 관찰하는 것이 가장 좋다.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(handleScreenConnectNotification:)
name:UIScreenDidConnectNotification object:nil];
[center addObserver:self selector:@selector(handleScreenDisconnectNotification:)
name:UIScreenDidDisconnectNotification object:nil];
return YES;
}
- 외부 화면에 window를 표시하려면 올바른 screen 개체와 연관시켜야 한다. UIScreen의 screen 메서드에서 screen 객체 목록을 가져올 수 있는데, 이 메서드가 return하는 배열에는 적어도 메인 screen을 나타내는 객체가 1개 포함된다. 두 번째 객체가 있으면 이것은 연결된 외부 디스플레이를 의미한다.
- window를 만들고 외부 디스플레이와 연결하고, window를 표시하기 전에 일부 placeholder 컨텐츠를 추가한다.
이 경우 placeholder content는 흰색 배경이고 표시할 컨텐츠가 없음을 나타내는 label이다.
window를 표시하기 위해서는
makeKeyAndVisible
을 호출하는 대신 hidden property 값을 변환한다.(새로운 window는 정적인 컨텐츠만 포함하고 이벤트 처리에는 사용되 않기 때문).
- (void)checkForExistingScreenAndInitializeIfPresent
{
if ([[UIScreen screens] count] > 1)
{
// Associate the window with the second screen.
// The main screen is always at index 0.
UIScreen* secondScreen = [[UIScreen screens] objectAtIndex:1];
CGRect screenBounds = secondScreen.bounds;
_secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
_secondWindow.screen = secondScreen;
// Add a white background to the window.
UIView* whiteField = [[UIView alloc] initWithFrame:screenBounds];
whiteField.backgroundColor = [UIColor blueColor];
[_secondWindow addSubview:whiteField];
// Go ahead and show the window.
_secondWindow.hidden = NO; //makeKeyAndVisible 대신.
}
}
-
전반적 프로세스
- 장치 방향 변경
- System의 supportedInterfaceOrientation 호출 (Window 내의 topmost presentedVC나 rootVC) (supportedInterfaceOrientation 메서드는 shouldAutorotate가 YES인 경우에만 호출된다.)
- 해당 메서드에서 지원하는 방향과 장치의 방향이 같은지 판별
- window와 vc의 방향 전환
-
Rotation은 ViewController의 view size가 변경된 것으로 간주되기 때문에,
viewWillTransition(to:with)
메서드에 의해 보고(?)된다. Interface의 방향이 변경되면 UIKit은 window의 rootViewController에서viewWillTransition(to:with)
를 호출한다. 그리고 childViewController에 알리고 ViewController 계층 전체에 전파한다.(어떤 식으로 전파하는거지?) 만약 viewController가 navigation Controller에 종속되어 있다면 navigation Controller의 룰을 따른다. -
ViewController는
supportedInterfaceOrientation
메서드를 override하여 지원하는 방향을 제한할 수 있다. 일반적으로 System은 이 메서드를 window의 rootViewController, 또는 화면 전체를 채우는 ViewController에서만 호출한다. 하위 ViewController는 상위 ViewController가 제공하는 window의 포지션을 사용한다. -
보이는 ViewController에 rotation이 발생하면
willRotate(to:duration:)
,willAnimateRotation(to:duration)
,didRotate(from:)
이 호출되고,viewWillLayoutSubviews()
는 view의 크기와 배치가 조정된 후에 호출된다. ViewController가 보이지 않으면 rotate관련 메서드는 호출되지 않지만, view가 보이게 되면viewWillLayoutSubviews()
가 호출된다. -
Rotation handling은 key window에서 이루어지며(시스템적으로), 다른 window에서도 handling하기 위해서는 직접 구현해야 한다고 한다(정확한지 확인 필요) (https://stackoverflow.com/a/28696091/7072456) (http://shaune.com.au/using-multiple-uiwindows-in-ios-applications/)