[iOS] UINavigationController 공식문서 번역
안녕하세요🙇♂️
이번 게시글은 UINavigationController 공식문서를 번역해보려고 합니다!
정말 많이 사용하는 ContainerViewController죠?
많이 사용하는만큼 정확히 알고 사용해보도록 합시다!
혹시나 번역이나 내용이 틀렸을 경우 댓글로 지적 부탁드립니다!😅
번역하면서 제 생각은 요 보라색으로 작성해볼테니까 헷갈리지 말고 읽어주세요! ㅎㅎ
그럼 시작해볼까요? 깔깔!
UINavigationController
계층적 콘텐츠 탐색을 위한 Stack 기반 체계를 정의하는 ContainerViewController입니다.
Declaration
@MainActor class UINavigationController: UIViewController
@MainActor는 iOS15부터 사용할 수 있는 annotation입니다.
WWDC2021에서 소개하고 있으니 궁금하신분들은 찾아보세요!
물론 다음에 @MainActor에 대해 게시글을 작성할 예정입니다 :)
overview
NavigationController는 Navigation Interface에 한개 이상의 Child ViewController를 관리하는 ContainerViewController입니다. 이 유형의 Interface에서는 한 번에 하나의 ChildViewController만 볼 수 있습니다. ViewController에서 Item을 선택하면 애니메이션을 사용하여 새로운 ViewController를 화면에 push하여 이전 ViewController를 숨깁니다. Interface 상단의 navigation bar에서 BackButton을 탭하면 상단 ViewController가 제거되고 그 아래에 이전 ViewController가 표시됩니다.
Navigation Interface를 사용하여 앱에서 관리하는 계층적 데이터 구성을 모방합니다. 계층 구조의 각 수준에서 해당 수준의 콘텐츠를 표시할 적절한 화면(Custom ViewController에서 관리)을 제공합니다. 그림 1은 iOS 시뮬레이터의 설정App 에서 제공하는 Navigation Interface의 예를 보여줍니다. 첫 번째 화면에는 기본 설정이 포함된 App 목록이 표시됩니다. 응용 프로그램을 선택하면 해당 응용 프로그램에 대한 개별 설정 및 설정 그룹이 표시됩니다. 그룹을 선택하면 더 많은 설정 등이 생성됩니다. Root View를 제외한 모든 Item에 대해 Navigation Controller는 사용자가 계층 구조 위로 다시 이동할 수 있도록 BackButton을 제공합니다.
NavigationController는 NavigationStack이라고 하는 정렬된 배열을 사용하여 ChildViewController를 관리합니다. 배열의 첫 번째 ViewController는 RootViewController이며 스택의 맨 아래를 나타냅니다. 배열의 마지막 ViewController는 스택의 최상위 Item이며 현재 표시되는 ViewController를 나타냅니다. segue를 사용하거나 이 클래스의 메서드를 사용하여 스택에서 ViewController를 추가 및 제거합니다. 사용자는 NavigationBar의 BackButton을 사용하거나 왼쪽 가장자리 SwipeGesture를 사용하여 최상위 ViewController를 제거할 수도 있습니다.
NavigationController는 Interface 상단의 NavigationBar와 하단의 선택적인 ToolBar를 관리합니다. NavigationBar는 항상 존재합니다. NavigationController 자체에 의해 관리되며, NavigationController는 ChildViewController에서 제공하는 콘텐츠를 사용하여 NavigationBar를 업데이트합니다. isToolbarHidden 속성이 false인 경우 NavigationController는 최상위 ViewController(현재 보이고 있는 VC)에서 제공하는 내용으로 ToolBar를 유사하게(similarly) 업데이트합니다.
아니!!! similarly하게 업데이트를 한다는게 무슨 말이죠... 하면 하는거고 아니면 아닌거지.. 왜이래요 도대체🤪
그리고 여기서 좀 이상한 점을 발견했습니다.
let navigationController = UINavigationController(rootViewController: ViewController())
navigationController.isToolbarHidden = false
let addToolBarItem = UIBarButtonItem(systemItem: .add)
let bookmarkToolBarItem = UIBarButtonItem(systemItem: .bookmarks)
let items = [bookmarkToolBarItem, addToolBarItem]
// 이 부분!!
navigationController.toolbar.items = items
navigationController.toolbar.setItems(items, animated: false)
navigationController.toolbarItems = items
navigationController.setToolbarItems(items, animated: false)
아니!!! 이렇게 다 toolBarItem을 세팅해줘도 안먹어요!!! 이유를 모르겠어요😭
많이 찾아봤는데 그냥 ChildViewController에서 setToolBarItems(:animated:)를 호출해서 적용하는 방법밖에 없는 것 같아요..
제가 발견하지 못한걸 수도 있으니 혹시나 아시는분께서는 댓글 부탁드립니다🤪 (당연히 저도 더 찾아보겠습니다!)
NaviagtionController는 delegate를 통해 동작을 조정합니다. delegate는 ViewController의 push 또는 pop을 재정의하고 Custom Transition Animation을 제공하며 Navigation Interface의 기본 방향(orientation)을 지정할 수 있습니다. 제공하는 delegate는 UINavigationControllerDelegate 프로토콜을 준수해야 합니다.
그림 2는 Navigation Controller와 이것이 관리하는 개체 간의 관계를 보여줍니다. Navigation Controller의 지정된 properties를 사용하여 이러한 개체들에 접근합니다.
NavigationController Views
NavigationController는 ContainerViewController입니다. 즉, 다른 ViewController의 content을 자체 내부에 포함합니다. view property를 통해 NavigationController의 view에 접근합니다. 이 view는 NavigationBar, Optional ToolBar 및 최상위 ViewController에 해당하는 content view를 통합합니다. 그림 3은 이러한 view를 조합하여 전체 Navigation Interface를 표시하는 방법을 보여줍니다.(이 그림에서 Navigation Interface는 TabBar Interface 내부에 추가로 포함되어 있습니다.) NavigationBar 및 ToolBar view의 내용은 변경되지만 view 자체는 변경되지 않습니다. 실제로 변경되는 유일한 view는 NavigationStack의 최상위 ViewController에서 제공하는 Custom content view입니다.
❗️iOS 7 이상에서는 ContentView가 NavigationBar 아래에 있기 때문에 ViewController의 contents를 디자인할 때 이 공간을 고려해야 합니다.
NavigationController는 NavigationBar 및 Optional ToolBar의 생성, 구성 및 표시를 관리합니다. NavigationBar의 appearance와 관련된 properties를 custom할 수 있지만 frame, bounds 또는 alpha 값을 직접 변경해서는 안됩니다. UINavigationBar를 서브클래싱하는 경우 init(navigationBarClass:toolbarClass:) 메서드를 사용하여 NavigationController를 초기화해야 합니다. NavigationBar을 숨기거나 표시하려면 isNavigationBarHidden 속성 또는 setNavigationBarHidden(_:animated:) 메서드를 사용합니다.
NavigationController는 NavigationStack의 ViewController와 연결된 NavigationItem 개체(UINavigationItem 클래스의 인스턴스)를 사용하여 NavigationBar의 contents를 동적으로 빌드합니다. NavigationBar의 전체적인 모양을 custom하려면 UIAppearance API를 사용합니다. 따라서 NavigationBar의 content를 변경하려면 Custom ViewController의 NavigationItem을 구성해야 합니다. NavigationItem에 대한 자세한 내용은 UINavigationItem을 참조하십시오.
Updating the NavigationBar
최상위 ViewController가 변경될 때마다 NavigationController는 그에 따라 NavigationBar를 업데이트합니다. 특히, NavigationController는 왼쪽, 중간 및 오른쪽의 세 가지 NavigationBar 위치 각각에 표시되는 BarButtonItem을 업데이트합니다. BarButtonItem은 UIBarButtonItem 클래스의 인스턴스입니다. 필요에 따라 Custom Content를 만들거나 일반적인 System Item을 만들 수 있습니다.
NavigationBar의 tint color는 NavigationBar 자체의 속성에 의해 제어됩니다. tintColor 속성을 사용하여 Bar Item의 tint color를 변경하고 barTintColor 속성을 사용하여 NavigationBar 자체의 tint color를 변경합니다. NavigationBar는 현재 표시된 뷰 컨트롤러에서 tint color를 상속하지 않습니다.
그니까 NavigationBar의 tint color와 관계없고!
navigationController.navigationBar.tintColor 를 사용하면 BarItem의 tint color가 변경되고!
navigationController.navigationBar.barTintColor 를 사용하면 NavigationBar 자체의 tint color가 변경된다 이말입니다!
헷갈리지 맙시다!
NavigationBar에 대한 자세한 내용은 UINavigationBar를 참조하세요. BarButtonItem을 만드는 방법에 대한 자세한 내용은 UIBarButtonItem을 참조하세요.
The Left Item
NavigationStack의 Root ViewController를 제외한 모든 항목에 대해 NavigationBar의 왼쪽에 있는 항목은 이전 ViewController로의 navigating을 제공합니다. 가장 왼쪽 버튼의 내용은 다음과 같이 결정됩니다.
- 새 최상위 ViewController에 Custom LeftBarButtonItem이 있는 경우 해당 Item이 표시됩니다. Custom LeftBarButtonItem을 지정하려면 ViewController의 navigationItem.leftBarButtonItem 속성을 설정하십시오.
- 최상위 ViewController에 Custom LeftBarButtonItem이 없지만 이전 ViewController의 NavigationItem에 backBarButtonItem 속성에 개체가 있는 경우 NavigationItem에 해당 Item이 표시됩니다.
- Custom BarButtonItem이 ViewController 중 하나에 의해 지정되지 않은 경우, Default Back Button 이 사용되며 title은 이전 ViewController의 title 속성 값으로 설정됩니다. 즉, 스택에서 한 수준 아래에 있는 ViewController입니다. (NavigationStack에 ViewController가 하나만 있는 경우 BackButton이 표시되지 않습니다.)
❗️BackButton의 title이 너무 길어 사용 가능한 공간에 맞지 않는 경우 NavigationBar에서 실제 button의 title을 "뒤로" ("back") 문자열로 대체할 수 있습니다. NavigationBar는 이전 ViewController에서 BackButton을 제공한 경우에만 이 작업을 수행합니다. 새 최상위 ViewController에 NavigationBar의 leftBarButtonItem 또는 leftBarButtonItems 속성이 Custom LeftBarButton으로 설정된 경우 NavigationBar는 button의 title을 변경하지 않습니다.
저는 navigationItem.backBarButtonItem을 지정하면 현재 ViewController의 backButton에 지정되는 줄 알았는데...
다른 ViewController가 생성되고 자신으로 돌아오는 BackBarButtonItem을 설정하는 것이었군요... 바보였습니다.. 왠지 안되더라..
이제라도 알았으니 다행인거죠...? ㅎㅎ.. 하나 알아갑니다!😅
The Middle Item
NavigationController는 NavigationBar의 중앙을 다음과 같이 업데이트합니다.
- 새 최상위 ViewController에 Custom TitleView가 있는 경우 NavigationBar는 Default TitleView 대신 해당 view를 표시합니다. Custom TitleView를 지정하려면 ViewController의 navigationBar.titleView 속성을 설정합니다.
- Custom TitleView가 설정되어 있지 않으면 NavigationBar에 ViewController의 Default Title이 포함된 label이 표시됩니다. 이 label의 문자열은 일반적으로 ViewController 자체의 title 속성에서 가져옵니다. ViewController와 연결된 title과 다른 title을 표시하려면 대신 ViewController의 navigationItem.title 속성을 설정하세요.
여기서도 이상한점이 있습니다.
ViewController와 연결된 title과 다른 title을 표시하려면 대신 ViewController의 navigationItem.title 속성을 설정하세요.
라고 말해서 저는 title을 설정해도 navigationItem.title을 설정하게 되면 navigationItem에 설정된 title이 나올 줄 알았는데..
override func viewDidLoad() {
super.viewDidLoad()
title = "Eternals"
navigationItem.title = "Spider Man"
}
이렇게 viewController의 title을 설정하고 navigationItem.title을 설정하면 원하는대로 나오지만
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Spider Man"
title = "Eternals"
}
요렇게 순서를 바꿔서 navigationItem.title을 먼저 설정하고 title을 설정하면 title이 그대로 나옵니다.
title과 관련해서 우선순위는 없는 것 같고 그냥 나중에 설정한 값이 title로 나오는 것 같습니다!
흠.. 뭔가 navigationItem.title 속성이 nil이 아니면 이거를 먼저 가져와주는것이 어떨까 싶지만..
더 좋은 어떤 이유가 있었겠죠??..?..?? 혹시나 아시는 분은 댓글로 부탁드립니다!🙇♂️
계속 공식문서를 읽어봅시다!
The Right Item
NavigationController는 NavigationBar의 오른쪽을 다음과 같이 업데이트합니다.
- 새로운 최상위 ViewController에 Custom RightBarButtonItem이 있는 경우 해당 item이 표시됩니다. Custom RightBarButtonItem을 지정하려면 ViewController의 navigationItem.rightBarButtonItem 속성을 설정하십시오.
- Custom RightBarButtonItem이 지정되지 않은 경우 NavigationBar는 오른쪽에 아무 것도 표시하지 않습니다.
Displaying ToolBar
NavigationController는 View Hierarchy에서 Optional ToolBar를 관리합니다. 표시될 때 이 ToolBar는 Active ViewController(현재 보이고 있는 최상위 ViewController를 얘기하는 것 같습니다! 아?)의 toolbarItem 속성에서 현재 item 모음(set)을 가져옵니다. Active ViewController가 변경되면 NavigationController는 새 ViewController와 일치하도록 ToolBarItem을 업데이트하고 적절한 경우 새 item을 해당 위치에 애니메이션으로 적용합니다.
NavigationToolBar는 기본적으로 hidden되어있지만 NavigationController의 setToolbarHidden(_:animated:) 메서드를 호출하여 Navigation Interface에 표시할 수 있습니다. 모든 ViewController가 ToolBarItem을 지원하지 않는 경우 delegate는 이 메서드를 호출하여 이후의 push 및 pop 작업 중에 ToolBar의 visibility를 toggle할 수 있습니다. UIToolBar class를 SubClass하려면 init(navigationBarClass:toolbarClass:) 메서드를 사용하여 navigation controller를 초기화합니다. Custom ToolBar 및 NavigationBar를 SubClassing하여 NavigationController를 만드는 경우 NavigationController를 화면에 표시하기 전에 뷰 컨트롤러를 푸시하고 설정해야 합니다.
let viewController = UIViewController()
let navigationController = UINavigationController(
navigationBarClass: CustomNavigationBar.self,
toolbarClass: CustomToolbar.self
)
navigationController.pushViewController(viewController, animated: false)
요런식으로 사용하라는 말이죠? ㅎㅎ
생성할 때 rootViewController를 설정해줄 수 없는 initializer를 사용하기 때문에
직접 push를 해줘야한다는 말 같습니다!
Adapting to Different Environments
Navigation Interface는 horizontally compact와 horizontally regular 환경 모두에서 동일하게 유지됩니다. 두 환경 사이를 전환할 때 NavigationController의 view 크기만 변경됩니다. NavigationController의 View Hierarchy나 View Layout을 변경하지 않습니다. Navigation Stack의 ViewController 간에 segue를 구성할 때 Show 및 Show Detail segue는 다음과 같이 작동합니다.
- Show segue - NavigationController가 지정된 ViewController를 NavigationStack으로 푸시합니다.
- Show Detail segue - NavigationController는 지정된 ViewController를 모달로 표시합니다.
다른 segue 유형의 동작은 변경되지 않습니다.
저도 최근에 알게된 메서드인데요!
@objc func showDetailSceneButtonTapped(_ sender: UIButton) {
// 이렇게 하면 Navigation Stack에 push
show(DetailViewController(), sender: self)
// 이렇게 하면 modal로 띄워줍니다!
showDetailViewController(DetailViewController(), sender: self)
}
이런식으로 show를 사용하게 되면 navigationController와의 의존성을 끊을 수 있어서 좋다는 내용을 봤습니다!
이 부분에 대해서는 나중에 꼭! 정리해서 게시하도록 하겠습니다! 매우 궁금하네요 ㅎㅎ
Interface Behaviors
NavigationController는 Interface에 대해 다음 동작을 지원합니다.
- Supported Interface Orientation
NavigationController는 지원되는 인터페이스 방향을 결정할 때 Navigation Stack의 ViewController를 참조하지 않습니다. iPhone에서 NavigationController는 portrait upside-down을 제외한 모든 방향을 지원합니다. iPad에서 NavigationController는 모든 방향을 지원합니다. NavigationController에 delegate가 있는 경우 delegate는 navigationControllerSupportedInterfaceOrientations(_:) 메서드를 사용하여 지원되는 방향을 지정할 수 있습니다.
- Presentation context
NavigationController는 modal로 표시되는 ViewController에 대한 프레젠테이션 컨텍스트를 정의합니다. Modal Transition Style이 UIModalPresentationStyle.currentContext 또는 UIModalPresentationStyle.overCurrentContext인 경우 NavigationStack에 있는 ViewController의 모달 프레젠테이션은 전체 Navigation Interface를 포함합니다.
State Preservation
NavigationController의 restoreIdentifier 속성에 값을 할당하면 NavigationStack에서 자신과 Child ViewController를 보존하려고 시도합니다. NavigationController는 stack의 맨 아래에서 시작하여 위쪽으로 이동하여 유효한 복원 식별자 문자열(valid restoration identifier string)이 있는 각 ViewController를 인코딩합니다. 다음 Launching Cycle 동안 NavigationController는 보존된 ViewController를 보존된 것과 동일한 순서로 Navigation Stack에 복원합니다.
Navigation Stack에 푸시하는 Child ViewController는 동일한 복원 식별자를 사용할 수 있습니다. NavigationController는 각 Child ViewController의 복원 경로가 고유하도록 추가 정보를 자동으로 저장합니다.
State Preservation 및 restoration works에 대한 자세한 내용은 Preserving Your App's UI Cross Launches을 참조하십시오.
자!
UINavigationController에 대한 공식문서는 요렇게 끝납니다!
물론 깊게 살펴봐야 될 내용이 산더미 같이 쌓였지만!
그건 이후의 게시글을 통해 진행할 예정입니다.
항상 공식문서를 보다보면 한 두개씩 놓치고 사용했던 내용들을 발견하게 되는 것 같습니다!
놓치고 계셨던 부분이 있었다면 이 글을 보시고 정확히 확인하실 수 있는 시간이 되셨으면 좋겠습니다 :)
부족한 글 끝까지 봐주셔서 감사합니다!
그럼 바위!👋
Reference
https://developer.apple.com/documentation/uikit/uinavigationcontroller