[UISegmentedControl] 상단 커스텀 탭바를 만들어봅시다! (1) - UISegmentedControl 활용

2023. 11. 26. 21:55UIKit, H.I.G

728x90

1️⃣ 오늘 만들어줄 화면은?


합동 세미나 과제로 테이블링 어플을 클론코딩하면서 만들었던 상단 커스텀 탭바의 내용을 정리해보겠다.

여러 글들을 찾아봤을 때, 상단 커스텀 탭바를 만들어주는 방법은 크게 두 가지가 있는 것 같았다.
CollectionView와 PageViewController를 함께 활용해서 만들어줄 수 있고, Segmented Control을 활용해서 만들어줄 수도 있었는데, 이번 글에서는 후자의 방식으로 구현하는 것을 소개해보겠다. (전자의 방식은 다음에 2탄으로 작성해보기로 하며)

두 방식 중 어떤 방식을 선택해야 하는가 했을 때, 구현 방식으로 본다면 Segmented Contol의 방식이 더 쉽겠지만, 이 방법은 각 탭 영역이 일정한 경우에만 사용할 수 있다는 제한사항이 있으므로 각 상황에 맞게 선택을 해야겠다.

아무튼 내가 구현할 뷰 스케치 화면을 살펴보게 되면 아래와 같다.

 

2️⃣ UISegmentedControl 세팅 (Custom)


기존 코드에 새로운 코드를...으로 추가해 주는 방식으로 설명해 보도록 하겠다.

우선 기본 Segmented Control Component를 생성해주자.
insertSegment를 통해서 Segment의 요소를 title과 index값을 포함해서 속성을 추가해주도록 하겠다.

    private let segmentControl: UISegmentedControl = {
        let segment = UISegmentedControl()
        segment.insertSegment(withTitle: "홈", at: 0, animated: true)
        segment.insertSegment(withTitle: "전체메뉴", at: 1, animated: true)
        segment.insertSegment(withTitle: "최근리뷰", at: 2, animated: true)
        segment.selectedSegmentIndex = 0   // 첫번째 segmented control의 인덱스 값
        return segment
    }()

다음은 위에서 추가해준 Segment 각 요소들의 text 속성을 지정해주겠다.

setTitleTextAttributes의 for 파라미터의 상태값에 따라 기본(.normal)과 선택(.selected)에 따른 속성을 각각 추가해주었다.
각 속성은 NSAttributedString의 foregroundColor(글자 색) Key와 Font(폰트) Key를 활용하는 방식이 사용된다.

    private let segmentControl: UISegmentedControl = {
        ...
        segment.setTitleTextAttributes([
            NSAttributedString.Key.foregroundColor: UIColor.Gray200,
            NSAttributedString.Key.font: UIFont.pretendardSemiBold(size: 16)
        ], for: .normal)
        segment.setTitleTextAttributes([
            NSAttributedString.Key.foregroundColor: UIColor.Gray800,
            NSAttributedString.Key.font: UIFont.pretendardSemiBold(size: 16)
        ], for: .selected)
        return segment
    }()

다음에는 Segment가 선택되었을 때, 변하는 tintColor를 제거(.clear)해주었다.

    private let segmentControl: UISegmentedControl = {
        ...
        segment.selectedSegmentTintColor = .clear
        return segment
    }()

마지막으로, SegmentedContol에서 주어지는 기본 이미지들을 제거해주겠다.
기본적으로 배경 이미지(backgroundImage)와 중간 칸막이 이미지(dividerImage)를 UIImage()로 setting해서 표출되지 않도록 설정했다.

    private let segmentControl: UISegmentedControl = {
        ...
        segment.setBackgroundImage(UIImage(), for: .normal, barMetrics: .default)
        segment.setDividerImage(UIImage(), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
        return segment
    }()

 

3️⃣ Indicator Bar (Underline View) 세팅


이제 위에서 구현해 준 탭바 밑에 따라다니게 될 Indicator Bar를 추가로 구현해주겠다.
우선, 아래와 같이 Component를 만들어주고, 기본적인 레이아웃을 잡아주었다.

    private let underLineView: UIView = {
        let view = UIView()
        view.backgroundColor = .Gray600
        return view
    }()

...

    underLineView.snp.makeConstraints {
        $0.top.equalTo(segmentControl.snp.bottom).offset(21)
        $0.width.equalTo(92)
        $0.height.equalTo(2)
        $0.leading.equalTo(segmentControl.snp.leading)
    }

이제 해줘야 할 작업은 segmentControl의 값이 변할 때마다 Indicator Bar(underLineView)의 leading값을 변화시켜, Bar가 움직이는 듯한 animation을 주는 것이다.

미리 말하지만, 이 부분은 사실 뒤에 리팩토링이 필요한 부분이다,
기기 대응에 있어 절대적인 수치 값으로 레이아웃을 잡아주었기 때문에, 이 수치값 대신 비율을 통한 계산식이 필요할 것이라 생각된다.
여기서는 일단 target을 추가해주고, 그와 연결된 objc function을 만든 것까지 작성해보겠다.

segmentControl.addTarget(self, action: #selector(changeSegmentedControlLinePosition(_:)), for: .valueChanged)

    @objc
    private func changeSegmentedControlLinePosition(_ segment: UISegmentedControl) {
        lazy var leadingDistance: CGFloat =  CGFloat(segmentControl.selectedSegmentIndex)*93
        UIView.animate(withDuration: 0.2, animations: {
            self.underLineView.snp.updateConstraints { $0.leading.equalTo(self.segmentControl.snp.leading).offset(leadingDistance)
            }
            self.layoutIfNeeded()
        })
    }

 

4️⃣ Segmented Contol Index 값에 따른 분기 처리로 View 표출 변경


이제 마지막으로 SegmentedControl의 index값에 따라 화면을 다르게 표출시켜보겠다.
여기서는 3개의 화면을 중첩시키고, isHidden 속성을 이용해서 뷰 화면을 다르게 보여주도록 구현했다.

segmentControl.addTarget(self, action: #selector(didChangeValue(_:)), for: .valueChanged)

    @objc
    private func didChangeValue(_ segment: UISegmentedControl) {
        switch segment.selectedSegmentIndex {
        case 0:
            homeView.isHidden = false
            allMenuView.isHidden = true
            recentReviewView.isHidden = true
        case 1:
            homeView.isHidden = true
            allMenuView.isHidden = false
            recentReviewView.isHidden = true
        default:
            homeView.isHidden = true
            allMenuView.isHidden = true
            recentReviewView.isHidden = false
        }
    }

 

5️⃣ 최종 구현 화면


728x90