UITabBarController
1. Docs
* 공식 문서의 내용은 한 번에 모두 추가하는 것이 아니라, 기본적인 내용을 먼저 작성한 후, 이후에 필요한 프로퍼티/메서드가 있을 경우 하나씩 추가할 예정.
https://developer.apple.com/documentation/uikit/uitabbarcontroller
Apple Developer Documentation
developer.apple.com
# Managing the view controllers
var viewControllers: [UIViewController]?
- An array of the root view controllers displayed by the tab bar interface.
func setViewControllers([UIViewController]?, animated: Bool)
- Sets the root view controllers of the tab bar controller.
UITabBarController를 사용하기 위해서는 크게 2가지 설정이 필요하다고 보면 된다.
- UIViewController
- UITabBarItem
이때 UIViewController와 UITabBarItem은 일대일 대응으로, UITabBarItem은 각각의 UIViewController에 접근할 수 있도록 도와주는 역할을 수행한다.
즉, UITabController을 상속받은 파일에서 TabController에 연결할 ViewController들을 선언하고, 각각의 ViewController에 UITabBarItem을 연결해주면 된다.
- UIViewController와 UITabBarItem을 attach 하는 방법?
extension UIViewController {
open var tabBarItem: UITabBarItem! // Automatically **created lazily with the view controller's title** if it's not set explicitly.
}
⇒ 각 UIViewController에서 tabBarItem 프로퍼티에 접근해 UITabBarItem을 매칭 시켜주면 된다.
UITabBarController에서 UITabBar 커스텀에 많이 사용되는 것은 tintColor 프로퍼티이다.
- tintColor
- unselectedItemTintColor
위 2가지 설정을 통해 UITabBarItem이 선택되었을 때와, 선택되지 않았을 때 색상 값 커스텀이 가능하다.
2. Apply
UITabBarController 기능을 구현하기 위해서 크게 3가지의 파일로 나누었다.
- UITabBarItem들을 관리하는 TabBarItem 파일.
- UITabBarController를 상속받아 TabBarController 역할을 수행하는 TabBarController 파일.
- UIViewController인 BaseTabViewController 파일
→ BaseTabViewController의 경우 POME 프로젝트에서 오른쪽 상단에 공통으로 버튼이 들어가 있기 때문에 추상화를 진행한 것이며, 따로 파일로 관리하지 않고 UIViewController를 상속받아 사용해도 상관없음
# TabBarItem.swift
- TabBarItem 열거형 구조를 통해 String 값과 icon 값 모두 하나의 자료형으로 관리가 가능하다.
enum TabBarItem: Int, CaseIterable {
case record
case review
case friend
case mypage
}
extension TabBarItem {
var title: String {
switch self {
case .record: return "기록"
case .review: return "회고"
case .friend: return "친구"
case .mypage: return "마이"
}
}
}
extension TabBarItem {
var inactiveIcon: UIImage? {
switch self {
case .record: return Image.recordInactivate
case .review: return Image.reviewInactivate
case .friend: return Image.friendInactivate
case .mypage: return Image.mypageInactivate
}
}
var activeIcon: UIImage? {
switch self {
case .record: return Image.recordActivate
case .review: return Image.reviewActivate
case .friend: return Image.friendActivate
case .mypage: return Image.mypageActivate
}
}
}
extension TabBarItem {
public func asTabBarItem() -> UITabBarItem {
return UITabBarItem(
title: title,
image: inactiveIcon,
selectedImage: activeIcon
)
}
}
- TabBarItem이 CaseIterable 프로토콜을 채택한 이유가 궁금하다면?
# TabBarController.swift
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setTabBarAppearance()
setTabBarItems()
}
private func setTabBarAppearance() {
UITabBar.appearance().tintColor = Color.main
UITabBar.appearance().unselectedItemTintColor = Color.grey_4
let fontAttributes = [NSAttributedString.Key.font: UIFont.autoPretendard(type: .sb_10)]
UITabBarItem.appearance().setTitleTextAttributes(fontAttributes, for: .normal)
}
func setTabBarItems(){
let tabs = [UINavigationController(rootViewController: RecordViewController(btnImage: Image.alarmActivate)),
UINavigationController(rootViewController: ReviewViewController(btnImage: Image.alarmActivate)),
UINavigationController(rootViewController: FriendViewController(btnImage: Image.addPeople)),
UINavigationController(rootViewController: MyPageViewController(btnImage: Image.setting))]
TabBarItem.allCases.forEach {
tabs[$0.rawValue].tabBarItem = $0.asTabBarItem() //UIViewController와 UITabBarItem 매칭
}
setViewControllers(tabs, animated: true)
}
}
위의 코드에서 한 가지 의문점이 발생할 수 있다.
TabBarItem을 열거형 타입으로 선언할 때, 왜 Int형으로 선언한 것인가?
답은 바로, index 값으로 사용하기 위해서이다.
TabBarItem.allCases.forEach {
tabs[$0.rawValue].tabBarItem = $0.asTabBarItem() //UIViewController와 UITabBarItem 매칭
}
TabBarController의 코드를 확인해보면 TabBarItem의 rawValue 값을 UIViewController 객체들을 모아둔 배열에 접근할 수 있도록 도와주는 index로 활용했음을 확인할 수 있다.
열거형 타입에 대해 잠깐 짚고 넘어가자면..
열거형 타입을 선언할 때 타입을 같이 정의할 경우, rawValue 값 타입을 지정한 것이다.
enum TabBarItem: Int, CaseIterable {
case record //rawValue = 0
case review //rawValue = 1
case friend //rawValue = 2
case mypage //rawValue = 3
}
TabBarItem에서는 rawValue가 Int형이 된 것이다.
위의 코드와 같이 rawValue값을 할당하지 않으면, 0부터 순차적으로 할당이 된다.
enum TabBarItem: Int, CaseIterable {
case record = 10 //rawValue = 10
case review //rawValue = 11
case friend //rawValue = 12
case mypage //rawValue = 13
}
만약 record에 10을 할당하면 record의 rawValue 값은 10이 되며, review의 rawValue 값은 지정 값을 넘기지 않았지만 11이 할당되어 있음을 확인할 수 있다.
3. Result



