[TIL / 25.02.06] 라이브러리 사용해서 상단 탭바 구현하기 1

2025. 2. 6. 20:44·iOS/Swift

 

개발 팀 애들끼리 개발 관련 포스트를 쓸 수 있게 블로그를 만들고 있는데 난 거기서 iOS를 담당한다.

예전부터 한참 애썼던 탭바.. 탭바 자체는 솔직히 그냥 만들수있는데 하단에 같이 나오는 뷰 .. 이 뷰들의 높이가 각각 다르고 각각 스크롤이 필요하다? 골치아파졌음.. 인스타그램 마이페이지 피드 쪽 생각하면 감이 올거다.

 

우선 탭바를 어떤 형태로 구현할까 고민했음

 

1. SegmentedControl + a, 일단 상단 탭바는 세그먼트 컨트롤이라는 컴포넌트가 가장 가깝다. 다만 이를 전에 커스텀해서 만들려니 벽 느껴서 일단 보류.

 

2. UICollectionView + PageView, 이게 커스텀으로 할때 구조가 될 것 같은데, indicator의 애니메이션이랑 각 뷰컨트롤러 간 전환하는 데에 있어 Delegate 관련 구현할 게 많아 빠르게 구현해야 하는 현 상황에서 기각.

 

3. Tabman. 탭바를 쉽게 구현할 수 있게 해주는 라이브러리다. 이것도 결국 PageViewController를 상속받는 형태에 있어서 2번과 크게 다를 건 없지만 미리 구현되어있는 기능들을 쓸 수 있다는 점이 메리트. 

 

https://github.com/uias/Tabman

 

GitHub - uias/Tabman: ™️ A powerful paging view controller with interactive indicator bars

™️ A powerful paging view controller with interactive indicator bars - uias/Tabman

github.com

 


원래 나는 MVVM 형태로 ViewController에 UI 관련 코드를 쓴다기보다 View 파일에 코드를 작성 후 ViewController에서 loadView 메서드를 통해 해당 뷰를 입히는 방향으로 진행해왔다. 다만, Tabman의 경우 자체로 뷰 구조가 잡혀있어서 ViewController에 Tabman을 상속시키고 내가 만든 View를 인스턴스화해 loadView()한다? 화면 구조가 깨짐.. 

 

private func configureTabBar(_ bar: TMBar.ButtonBar) {
       bar.layout.transitionStyle = .progressive
       bar.layout.alignment = .leading
       bar.layout.contentMode = .intrinsic
       bar.layout.interButtonSpacing = 16
       bar.layout.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
       
       bar.backgroundView.style = .flat(color: .white)
       
       bar.indicator.weight = .custom(value: 2)
       bar.indicator.tintColor = .black
       
       bar.buttons.customize { button in
           button.tintColor = .systemGray
           button.selectedTintColor = .black
           button.font = .systemFont(ofSize: 16)
           button.selectedFont = .systemFont(ofSize: 16, weight: .bold)
       }
   }

 

탭바에 대한 설정은 이렇게 해두었다.

contentMode를 intrinsic으로 해서 좌측에 몰려있는 탭바 형태를 구현하였음.

균일하게 뷰를 꽉 채우는 형태로 하고 싶으면 fit으로 설정하면 된다.

 

private func setupTabman() {
       self.dataSource = self
       
       let bar = TMBar.ButtonBar()
       configureTabBar(bar)
       addBar(bar, dataSource: self, at: .custom(view: tabBarContainer, layout: nil))
       
       scrollToPage(.at(index: 0), animated: false)
   }

 

요렇게 Tabman 관련한 delegate를 설정해주고 저 탭바인 ButtonBar 인스턴스 생성 후 위에 있던 설정을 입혀준다.

addBar 매개변수 중 at에서는 보통 다들 .top으로 하는데 이 경우 화면의 최상단에 붙이고 싶으면 그렇게 하지만, 나는 그보다 위에 헤더뷰를 두어야 할 필요가 있었기에 custom했다. 이 부분엔 구글링해보니 자료가 많이 없어서 클로드한테 많이 도움받음..

 

tabBarContainer에 붙이는 건데 이 tabBarContainer는 어디에 있느냐

 

private func setupHeaderView() {
       view.addSubview(headerView)
       headerView.addSubview(logo)
       headerView.addSubview(editButton)
       headerView.addSubview(menuButton)
       view.addSubview(tabBarContainer)
       
       let safeAreaLayoutHeight = UIApplication.shared.connectedScenes
               .compactMap { ($0 as? UIWindowScene)?.keyWindow }
               .first?.safeAreaInsets.top ?? 0
       
       print("safeAreaHeight = \(safeAreaLayoutHeight)")
       
       headerView.snp.makeConstraints {
           $0.top.leading.trailing.equalToSuperview()
           $0.height.equalTo(60 + safeAreaLayoutHeight)
       }
       
       logo.snp.makeConstraints {
           $0.centerY.equalToSuperview().offset(safeAreaLayoutHeight/2)
           $0.leading.equalToSuperview().offset(20)
       }
       
       menuButton.snp.makeConstraints {
           $0.centerY.equalToSuperview().offset(safeAreaLayoutHeight/2)
           $0.trailing.equalToSuperview().offset(-20)
           $0.size.equalTo(24)
       }
       
       editButton.snp.makeConstraints {
           $0.trailing.equalTo(menuButton.snp.leading).offset(-16)
           $0.centerY.equalToSuperview().offset(safeAreaLayoutHeight/2)
           $0.size.equalTo(24)
       }
       
       tabBarContainer.snp.makeConstraints {
           $0.top.equalTo(headerView.snp.bottom)
           $0.leading.trailing.equalToSuperview()
           $0.height.equalTo(36)
       }
       
   }

 

headerView 바로 하단에 붙여두었음. 탭바 자체 높이는 36이기에 저렇게 설정해줬고 tabman 특성상 그 뒤에 관련 ViewController를 배치하는 것 같아서 headerView가 safeArea영역까지 커버할 수 있도록 했다. ( 커버 안하면 safeArea자리에 뷰가 뚫려서 보일 수 있음..)

 

centerY를 기준으로 배치하기 때문에 safeAreaLayoutHeight에 대해서도 반 나누어 적용함.

 

 

요런 구조인데 저 safeArea를 커버하지 않으면,,, 파란색이 보인다.. 그래서 결국 실제 뷰는 저 탭 아래부터 있는게 아닌 최상단부터 있지만 상위에 뷰가 가리고 있어 하단부터 시작되는것처럼 보일뿐일거다. 이 부분은 실제 뷰 구현시 참고해서 애초에 컴포넌트를 아래에 배치하던지 아니면 뷰컨트롤러 자체를 저 탭 아래부터 시작할 수 있도록 할 수 있는지 찾아봐야 알 것 같다.

 

그냥 Tabman을 사용하는 방법은

TMBar.ButtonBar()부분처럼 인스턴스 생성해주고 관련 설정 입혀주고 원하는 곳에 addBar해주면 된다. (+ 각 탭마다 연결될 뷰컨트롤러들 배열에 준비해두기)

 


 

이제 위에서 말했듯이 하위에 배치된 ViewController의 영역을 바꿀수 있는지 찾아보고 이 부분 조치 후 각 뷰마다 UI 구성해서 띄워봐야 알듯하다. 그냥 화면 안으로 들어가는 UI면 모르겠지만 세 탭 전부 스크롤할 수 있어야 하기에 UIScrollView, UITableView를 써서 뷰 구성 후 넣어 테스트해봐야 한다.

'iOS > Swift' 카테고리의 다른 글

[TIL / 25.02.11] 사이드메뉴를 구현해보자 . . . !  (0) 2025.02.11
[TIL / 25.02.10] 헤더를 이용한 테이블뷰를 구성, 셀과 헤더 높이 자동 계산  (4) 2025.02.10
[TIL / 25.02.07] 라이브러리 사용해서 상단 탭바 구현하기 2  (0) 2025.02.07
[TIL / 24.02.05] 함수의 기초 복습해봅시다 !  (5) 2025.02.05
[TIL / 25.02.04] Swift의 기본 데이터 타입~반복문까지 복습!  (0) 2025.02.04
'iOS/Swift' 카테고리의 다른 글
  • [TIL / 25.02.10] 헤더를 이용한 테이블뷰를 구성, 셀과 헤더 높이 자동 계산
  • [TIL / 25.02.07] 라이브러리 사용해서 상단 탭바 구현하기 2
  • [TIL / 24.02.05] 함수의 기초 복습해봅시다 !
  • [TIL / 25.02.04] Swift의 기본 데이터 타입~반복문까지 복습!
subkyu-ios
subkyu-ios
subkyu-ios 님의 블로그 입니다.
  • subkyu-ios
    subkyu-ios 님의 블로그
    subkyu-ios
  • 전체
    오늘
    어제
    • 분류 전체보기 (56)
      • iOS (38)
        • Swift (38)
      • 내일배움캠프 (7)
      • Git, Github (3)
      • Algorithm (6)
      • 회고 (1)
      • 면접 질문 정리 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    KPT
    til
    본캠프
    내일배움캠프
    ios
    회고
    UIKit
    의존성 주입
    사전캠프
    github
    Swift
    프로그래머스
    tabman
    알고리즘
    트러블슈팅
    stackview
    Wil
    algorithm
    TableView
    RxSwift
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
subkyu-ios
[TIL / 25.02.06] 라이브러리 사용해서 상단 탭바 구현하기 1
상단으로

티스토리툴바