기어가더라도 제대로

[UIKit-응용] Tab 전환을 모달로 하기(UITabBarController, Modal, Sheet) 본문

UIKit 기초

[UIKit-응용] Tab 전환을 모달로 하기(UITabBarController, Modal, Sheet)

Damagucci-juice 2025. 2. 4. 23:51

구현 목표

스레드 앱처럼 탭 바 가운데에 + 버튼의 화면 전환이 모달식으로 구현하는 것이 목표

모달 탭바 컨트롤러 구현

  • SceneDelegate에서 코드로 화면 구현
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = SheetableTabBarController.shared
        self.window = window
        self.window?.makeKeyAndVisible()
    }

}
  • SheetableTabBarController 선언
class SheetableTabBarController: UITabBarController {
    static let shared = SheetableTabBarController()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        setupTabBar()
    }
}
  • TabBar Icon을 편하게 넣도록 인덱싱 열거형 객체를 중첩 타입으로 선언
// in SheetableTabBarController

// 탭바 선언용 인덱스 열거형
enum ViewControllerIndex: Int, CaseIterable {
    case red = 0
    case green
    case blue

    var name: String {
        switch self {
        case .red:
            return "Red"
        case .green:
            return "Add"
        case .blue:
            return "Blue"
        }
    }

    var image: String {
        switch self {
        case .red:
            return "star.slash"
        case .green:
            return "plus.app"
        case .blue:
            return "moon.stars"
        }
    }
}
  • setupTabBar 구현, 일반적으로 탭뷰 구성하는 것과 같음
    • iOS 18에서 버그인지 확실하지 않으나 탭바 아이콘 표시가 안되어서 다른 방식으로 분기처리, 관련 유투브
// in SheetableTabBarController

func setupTabBar() {
    // UITabBar 불투명하게 설정
    let appearance = UITabBarAppearance()
    // 불투명한 배경 설정
    appearance.configureWithOpaqueBackground()
    appearance.backgroundColor = .white

    tabBar.standardAppearance = appearance

    // iOS 15 이상에서는 scrollEdgeAppearance도 설정해야 함
    if #available(iOS 15.0, *) {
        tabBar.scrollEdgeAppearance = appearance
    }

    // 뷰 생성
    let redViewController = UIViewController()
    redViewController.view.backgroundColor = .red
    let redNaviController = UINavigationController(rootViewController: redViewController)

    let dummyViewController = UIViewController()

    let blueViewController = UIViewController()
    blueViewController.view.backgroundColor = .blue
    let blueNaviController = UINavigationController(rootViewController: blueViewController)

    let viewControllers = [redNaviController, dummyViewController, blueNaviController]

    // 뷰컨트롤러들을 탭바 뷰컨트롤러에 연결
    setViewControllers(viewControllers, animated: false)

    // 개별 탭바 아이콘 설정
    if #available(iOS 18.0, *) {
        let allViewIndex = ViewControllerIndex.allCases
        for (index, child) in viewControllers.enumerated() {
            let currentViewIndex = allViewIndex[index]
            child.tabBarItem = UITabBarItem(
                title: currentViewIndex.name,
                image: UIImage(systemName: currentViewIndex.image),
                tag: currentViewIndex.rawValue
            )
        }
    } else {
        if let items = tabBar.items, items.count < 3 {
            ViewControllerIndex.allCases.forEach {
                items[$0.rawValue].title = $0.name
            }
        }
    }
}
  • UITabBarControllerDelegate를 채택하여서 몇 번째 탭이 선택되었을 때 할 행동 선언
    • shouldSelect 메서드를 선언해서 탭 선택을 미리 사전에 허용하거나 막기
    • 주의⚠️: 매개변수로 전달받은 viewController를 직접 모달로 넘기려고하면 에러가 남
      • 탭 화면을 생성해서 넘겨야함
extension SheetableTabBarController: UITabBarControllerDelegate {
    func tabBarController(
        _ tabBarController: UITabBarController,
        shouldSelect viewController: UIViewController
    ) -> Bool {

        if let viewControllers = tabBarController.viewControllers,
           let index = viewControllers.firstIndex(of: viewController),
           // 1번째 탭일 때
           index == 1 {

            // 초록색 뷰컨 설정
            let modalVC = UIViewController()
            modalVC.view.backgroundColor = .green
            let modalNavigationController = UINavigationController(rootViewController: modalVC)
            // 화면 전환 방식 지정
            modalNavigationController.modalPresentationStyle = .pageSheet

            if let sheet = modalNavigationController.sheetPresentationController {
                sheet.detents = [.large()]
                // 상단 손잡이 표시
                sheet.prefersGrabberVisible = true
            }

            present(modalNavigationController, animated: true)

            // 탭 방지
            return false
        }
        // 이외 탭은 탭 허용
        return true
    }
}

 

전체 코드

https://github.com/Damagucci-Juice/ExExtraordinaryTab

Comments