상세 컨텐츠

본문 제목

UITabBarController

iOS

by 쑤야. 2022. 11. 6. 22:15

본문

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가지 설정이 필요하다고 보면 된다.

 

  1. UIViewController
  2. 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 프로퍼티이다.

 

  1. tintColor
  2. unselectedItemTintColor

위 2가지 설정을 통해 UITabBarItem이 선택되었을 때와, 선택되지 않았을 때 색상 값 커스텀이 가능하다.


2. Apply

UITabBarController 기능을 구현하기 위해서 크게 3가지의 파일로 나누었다.

 

  1. UITabBarItem들을 관리하는 TabBarItem 파일.
  2. UITabBarController를 상속받아 TabBarController 역할을 수행하는 TabBarController 파일.
  3. 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 프로토콜을 채택한 이유가 궁금하다면?

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

 


관련글 더보기