[iOS Study] 자동 회전, 팝오버 컨트롤러, 모달 뷰 컨트롤러 |
출처 : 아론 힐리가스의 iOS 프로그래밍
-
이 장에서는 아래의 주제를 다룬다.
장치 의존적인 코드를 작성하는 방법과 장치의 종류에 따라 테스트하는 방법
회전, 팝오버 컨트롤러, 모달 뷰 컨트롤러
-
iOS 에서는 방향을 장치방향 (device orientation) 과 인터페이스 방향(interface orientation) 두 가지로 구분한다.
-
장치 방향은 장치 표면이나 후면에서 정방향, 뒤집힌 상태, 왼쪽 회전, 오른쪽 회전인지에 따른 물리적 방향을 나타낸다.
UIDevice 클래스의 orientation 프로퍼티를 통해 장치의 방향에 접근할 수 있다.
-
인터페이스 방향은 실행 중인 프로그램의 프로퍼티이다.
UIInterfaceOrientationPortrait 홈 버튼의 화면 아래쪽에 있다.
UIInterfaceOrientationPortraitUpsideDown 홈 버튼이 화면 위쪽에 있다.
UIInterfaceOrientationLandscapeLeft 장치가 옆으로 뉘여 있고 홈 버튼은 화면 오른쪽으로 향한다.
UIInterfaceOrientationLandscapeRight 장치가 옆으로 뉘여 있고 홈 버튼은 화면 왼쪽으로 향한다.
-
앱의 인터페이스 방향이 바뀌면 앱의 윈도우 크기 또한 바뀐다.
윈도우는 새로운 크기를 얻고 자신의 뷰 계층구조를 회전시킬 것이다.
계층구조상의 뷰들은 그들의 제약조건에 따라 다시 놓이게 된다.
-
장치 방향이 바뀌면 앱은 새로운 방향에 대해 알게 된다.
앱은 자신의 인터페이스 방향을 장치의 새 방향과 일치시키는 것을 허용할지 정할 수 있다.
-
Target 정보의 General 탭에서 Deployment Info 의 Device Orientation 섹션에서 지원할 방향을 설정할 수 있다.
기본으로는 Upside Down 외에는 체크(활성화)가 되어 있다.
-
아이패드 앱은 네 방향 모두로 회전할 수 있는 것이 일반적이다.
반면 아이폰 앱은 뒤집힌 상태를 제외한 나머지 방향으로만 회전할 수 있다.
iPhone/iPod Deployment Info 섹션을 통해 설정할 수 있다.
일부 앱들은 특정 방향에 대해 사용자가 접근할 수 없도록 잠그길 원한다.
예를 들어, 많은 게임들은 두 가지 가로 방향만 허용하고, 많은 아이폰 앱들은 세로 방향만 허용한다.
Device Orientation 버튼을 토글하여 자신의 앱에서 어떤 방향을 유효하게 할지 선택할 수 있다.
-
각 뷰 컨트롤러는 지원하는 모든 인터페이스 방향을 반환하는 메소드를 구현한다.
인터페이스 방향을 변경하기 위해서는 앱(각 info 프로퍼티 리스트의 Supported Interface Orientation 섹션마다)과 앱의 rootViewController 둘 다 세 방향에 반드시 동의해야 한다.
-
방향 허용을 바꾸고 싶다면 뷰 컨트롤러에서 아래 supportedInterfaceOrientations 를 재정의해주어야 한다.
아래는 supportedInterfaceOrientations 의 기본 구현.
- (NSUInteger) supportedInterfaceOrientations{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
return UIInterfaceOrientationMaskAll;
} else{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
UIUserInterfaceIdiomPhone 와 UIUserInterfaceIdiomPad 가 있다.
return 값에는 UIInterfaceOrientationMaskXxx 를 | operation 으로 묶어서 return 하면 된다.
-
UINavigationController 에서 회전 마스크를 결정하려면 UINavigationController 의 하위 클래스를 만들어 재정의해야 한다.
@implementation MyNavigationController
- (NSUInteger)supportedInterfaceOrientations{
return self.topViewController.supportedInterfaceOrientations;
}
@end
-
UITabViewController 는 각 탭의 뷰 컨트롤러에게 지원 인터페이스 방향을 요청하고,
그 교집합을 반환한다.
즉, UITabViewController 는 모든 탭이 지원하는 방향만을 지원한다.
-
cf)
Option + Click 은 보조 편집기를 연다.
xib 파일에서 Control + Drag 를 하면 손쉽게 outlet 이나 action 을 만들 수 있다.
-
방향 변경이 되었을 때 어떤 Action 을 하려면, UIViewController 의 willAnimateRotationToInterfaceOrientation:duration: 메소드를 재정의해아 한다.
첫번째 인자가 새 인터페이스 방향 값이며,
인터페이스 방향이 성공적으로 변경되면 뷰 컨트롤러가 이 callback 을 받는다.
이 메소드에서 뷰의 뭔가를 변경하는 코드(frame 을 hidden 시킨다던지)를 작성하면
이러한 변경은 애니메이션화된다.
duration 인자는 그 애니메이션이 얼마나 지속될지를 알려준다.
cf) UIInterfaceOrientationIsLandscape( orientation ) 을 통해 orientation 이 landscape 인지 알 수 있다.
[UIApplication sharedApplication] statusBarOrientation]; 을 통해 현재 orientation 값을 얻어올 수 있다.
-
뷰에 대한 작업이 아닌 다른 작업을 하는 코드가 이곳에 들어간다면,
혹은 뷰의 애니메이션화를 원하지 않는다면
willRotateToInterfaceOrientation:duration: 메소드를 재정의하면 된다.
인자에 들어오는 값은 willAnimateRotationToInterfaceOrientation:duration: 와 동일하지만
자동 animation 화 하지 않는다.
-
회전이 끝난 후 뭔가를 하고 싶다면 didRotateFromInterfaceOrientation: 메소드를 재정의하면 된다.
전달되는 인자는 회전이 발생하기 전의 예전 인터페이스 방향.
-
callback 형태가 아닌 현재 방향을 가져오는 또 다른 방법은
UIInterfaceOrientation io = [[UIApplication sharedApplication] statusBarOrientation];
ViewController.interfaceOrientation
-
팝오버 컨트롤러(popover controller)는 앱의 다른 인터페이스 위에 경계선을 가진 윈도우를 띄워 또 다른 뷰 컨트롤러의 뷰를 표시한다.
이는 아이패드에서만 가능하다. ( 아이폰에서 만들려고 하면 예외를 던진다. )
UIPopoverController 를 만들 때 다른 뷰 컨트롤러를 이 팝오버 컨트롤러의 contentViewController 로 설정한다.
팝오버 컨트롤러는 화면에서 사용자에게 선택 목록을 제시할 때(사진 보관함에서 사진을 고르는 경우 등)나 요약된 것에 관한 추가적인 정보를 줄 때 유용한다.
-
UIPopoverController 를 사용하는 경우 UIPopoverControllerDelegate 를 사용하는 UIViewController 에 프로토콜 지정해준다.
-
아래의 코드로 UIPopoverController 를 launch 시킨다.
UIPopoverController *controller = [[UIPopoverController alloc] initWithContentViewController:viewController];
controller.delegate = self;
[controller presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
-
PopoverController 가 떠 있을 때 해당 controller 바깥의 영역을 누르면 dismiss 되며
다음 delegate callback 을 부른다. popoverControllerDidDismissPopover:
PopoverController 에 isPopoverVisible 을 통해 이미 pop over 가 떠 있는지 확인할 수도 있다.
-
팝오버 컨트롤러에 명시적으로 dismissPopoverAnimated: 메시지를 보내면 팝오버 컨트롤러는 popoverControllerDidDismissPopover: 메시지를 자신의 델리게이트에 보내지 않는다.
-
Modal ViewController 를 띄울 때, 네비게이션용으로 사용하지 않을지라도 UINavigationController 의 인스턴스를 만들면, 모든 뷰들의 상단에 있는 것과 같은 타이틀바를 뷰에 제공할 수 있다.
-
모달로 표시된 뷰 컨트롤러를 닫으려면 dismissViewControllerAnimated:completion: 메시지를 반드시 그 뷰 컨트롤러에 보내야 한다.
-
모든 UIViewController 는 자신을 띄운 뷰 컨트롤러를 가리키는 presentingViewController 프로퍼티를 가진다.
( 엄밀히 이야기하면 자신을 띄운 뷰 컨트롤러의 Container 를 가르킨다. )
-
model 은 기본적으로 아래서부터 animation 되어 나타난다.
pushViewController:animated: 가 아닌 presentViewController:animated:completion: 함수를 통해 호출한다.
-
아이폰이나 아이팟 터치에서 모달 뷰 컨트롤러는 화면 전체를 차지한다.
-
아이패드에는 모달뷰 컨트롤러에 대한 두 가지 추가 옵션이 있다.
폼 시트 스타일(UIModalPresentationFromSheet) 나 페이지 시트 스타일 (UIModalPresentationPageSheet) 이다.
modalPresentationStyle 프로퍼티에 assign 하면 된다.
-
폼 시트 스타일은 아이패드 화면 중앙의 사각 영역에서 모달 뷰 컨트롤러를 보여주고, 기존 뷰 컨트롤러의 뷰는 어둡게 한다.
-
페이지 시트 스타일은 세로 모드에서 기본 풀 스크린 스타일과 동일하다.
반면에 가로 모드에서는 세로 모드와 같은 너비를 유지하고 그 뒤에 표시된 뷰 컨트롤러의 뷰의 왼쪽과 오른족 여백을 어둡게 한다.
-
modal 이 전체화면을 덮지 않으면, 호출한 viewController 의 viewWillAppear: 와 viewDidAppear: 함수는 불리지 않는다.
-
@property (nonatomic, copy) void (^dismissBlock)(void);
위의 코드를 통해 재사용 가능한 block 을 정의할 수 있다.
-
block 은 ^{ ... } 의 형태로 정의된다.
-
모달 뷰 컨트롤러의 화면에 나타나는 애니메이션도 변경할 수 있다.
뷰 컨트롤러 프로퍼티 modalTransitionStyle 에 미리 정의된 상수를 지정해주면 된다.
기본 애니메이션은 모달 뷰 컨트롤러를 화면 아래에서부터 위로 올라오도록 한다.
뷰 컨트롤러에 fade in, flip in, page curl 등의 효과를 줄 수 있다.
아래는 상수이다.
UIModalTransitionStyleCoverVertical
UIModalTransitionStyleCrossDissolve
UIModalTransitionStyleFlipHorizontal
UIModalTransitionStylePartialCurl
-
MultiThread 환경에서 안전한 singleton 을 만들기 위해서는..
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// singleton initialization
});
-
뷰 컨트롤러들 간의 관계는 뷰 컨트롤러의 뷰가 화면 어디에 어떻게 나타나는지 이해하는데 중요하다.
전체적으로 뷰 컨트롤러들 간의 관계는 두 종류가 있다.
부모-자식 관계와 프리젠팅-프레젠터 관계이다.
-
부모-자식 관계는 뷰 컨트롤러 컨테이너를 사용할 때 형성된다.
뷰 컨트롤러 컨테이너의 예로 UINavigationController, UITabBarController, UISplitViewController 가 있다.
뷰 컨트롤러 컨테이너는 자신이 포함한 뷰 컨트롤러들의 배열인 viewControllers 프로퍼티를 가지고 있다.
-
뷰 컨트롤러 컨테이너는 항상 UIViewController 의 하위 클래스이다.
따라서 뷰를 가진다.
뷰 컨트롤러 컨테이너는 viewControllers 의 뷰들을 선별적으로 자신이 소유한 뷰의 하위뷰로 추가하는 동작을 한다.
컨테이너는 내장된 인터페이스도 소유한다.
예를 들어, UINavigationController 의 view 는 내비게이션바와 topViewController 뷰를 보여준다.
-
부모-자식 관계의 뷰 컨트롤러들은 집단(family)을 형성한다.
그리고 집단은 여러 단계를 가질 수 있다.
예를 들어, UITabBarController 가 UIViewController 를 가진 UINavigationController 를 포함할 수 있다.
이들 세 뷰 컨트롤러는 같은 집단이다.
-
컨테이너 클래스는 그들의 viewControllers 배열을 통해 그 자식들에게 접근하고
자식들은 UIViewController 의 네 개의 프로퍼티를 통해 그 조상들에게 접근한다.
parentViewController 는 해당 집단에서 가장 가까운 조상 뷰 컨트롤러를 가르킨다.
집단 트리의 구성에 따라 UINavigationController, UITabBarController 또는 UISplitViewController 를 반환할 수 있다.
-
navigationController, tabBarController, splitViewController 메소드를 통해 조상에 접근할 수 있다.
뷰 컨트롤러가 이들 메시지 중 하나를 받으면 적절한 타입의 뷰 컨트롤러 컨테이너를 찾을 때까지 집단 트리를 검색한다. ( parentViewController 이용 )
적절한 타입의 조상이 없으면 nil 을 return 한다.
-
뷰 컨트롤러가 모달로 나타날 때는 부모-자식 관계가 아닌 표시자-피표시자의 관계가 된다.
뷰 컨트롤러가 모달로 표시될 때 표시된 뷰 컨트롤러의 view 는 그것을 표시한 뷰 컨트롤러의 view 맨 위에 추가된다.
이것은 뷰 컨트롤러 컨테이너와는 다르다.
모든 UIViewController 는 다른 뷰 컨트롤러를 모달로 나타낼 수 있다.
-
표시자와 피표시자 간의 관계를 관리하는 두 개의 내부 프로퍼티가 있다.
모달로 나타낸 뷰 컨트롤러의 presentingViewController 는 표시자 뷰 컨트롤러를 가리키고,
표시자의 presentedViewController 는 피표시자 뷰 컨트롤러를 가르킨다.
-
표시된 뷰 컨트롤러와 표시자는 같은 뷰 컨트롤러 집단이 아니다.
대신에 표시된 뷰 컨트롤러는 자기 소유의 집단을 가진다.
때때로 이 집단은 하나의 UIViewController 로 이뤄지고, 그렇지 않으면 여러 뷰 컨트롤러로 이뤄진다.
-
부모-자식 관계의 프로퍼티들은 절대 집단 경계를 넘어갈 수 없다.
-
뷰 컨트롤러가 모달로 나타나면 실제 표시자는 표시자 집단의 가장 오래된 멤버이다.
presentViewController:animated:completion: 를 부른 ViewController 가 반드시 표시자가 되지는 않는다.
이 관계가 UINavigationController 의 스택에서 일반적으로 나타날 때가 아닌 모달로 나타날 때 UINavigationBar 를 가리는 이유를 설명한다.
-
presentingViewController 와 presentedViewController 는 각 집단의 모든 뷰 컨트롤러에 유효하고 항상 상대 집단의 선조를 가리킨다.
실제로 이러한 선조를 가리키는 동작은 "아이패드에서만" 재정의할 수 있다.
이렇게 해서 화면에 나타낼 뷰 컨트롤러 집단의 뷰들을 어디에서 나타낼지 지정할 수 있다.
예를 들어 UINavigationBar 가 아닌 topViewController 부분만 가리면서 modal viewController 를 띄울 수 있다.
-
모든 UIViewController 는 definesPresentationContext 프로퍼티를 가진다.
기본적으로 이 프로퍼티는 NO 이다.
즉, 뷰 컨트롤러는 항상 남은 조상이 없을 때까지 다음 조상에게 계속 프레젠테이션을 전달한다.
이 프로퍼티가 YES 로 설정되면 선조를 검색하는 것을 중단하고
뷰 컨트롤러에 자신이 소유한 뷰에서 모달 뷰 컨트롤러를 표시하도록 허용한다.
추가로, 표시될 뷰 컨트롤러를 위해 modalPresentationStyle 을 UIModalPresentationCurrentContext 로 반드시 설정해야 한다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[iOS Study] 저장, 읽기, 앱 상태 (0) | 2016.03.07 |
---|---|
[iOS Study] 애니메이션 제어 (0) | 2016.03.06 |
[iOS Study] 오토 레이아웃 : 프로그래밍으로 제약조건 만들기 (0) | 2016.03.04 |
[iOS Study] 오토 레이아웃 소개 (0) | 2016.03.03 |
[iOS Study] 디버그 도구 (0) | 2016.02.26 |
댓글