ios/UI구현

[iOS, Swift] 네이버 웹툰 메인 페이지(UIPageViewController, UISegnmentControl 활용)

2023. 7. 27. 18:46
목차
  1. 구현 아이디어
  2. 구현과정
  3. 결과화면

네이버 웹툰 홈 화면 (세그먼트 컨트롤 사용, 하단 바 중점)

페이지 뷰 컨트롤러를 사용하여 페이징되는 화면을 구현하고

좌우 스크롤시 x값에 따라 움직이는 언더바를 어떻게 구현할지 생각해본다.

구현 아이디어

SegnmentControl 과 UIPageViewController를 이용하여 제스처나 버튼을 통해 페이지 이동시 segmentControl 의 언더바 또한 페이징 동작에 맞춰 동적으로 이동할 수 있게 하였다.

구현과정

먼저 세그먼트 컨트롤을 커스텀 하는 과정입니다 (참고: https://ios-development.tistory.com/963)

import UIKit

class UnderlineSegmentedControl: UISegmentedControl {

    //underlineView 추가(autolayout이 아닌, frame을 이용하므로 layoutSubviews에서 호출
    lazy var underlineView: UIView = {
        let width = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let height = 2.0
        let xPosition = CGFloat(self.selectedSegmentIndex * Int(width))
        let yPosition = self.bounds.size.height - 4.0
        let frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
        let view = UIView(frame: frame)
        view.restorationIdentifier = "underbar"
        view.backgroundColor = .green
        self.addSubview(view)
        return view
    }()
    
    
    
    func removeBackgroundAndDivider() {
        let image = UIImage()
        //회색 배경과 divider를 지우는 코드
        self.setBackgroundImage(image, for: .normal, barMetrics: .default)
        self.setBackgroundImage(image, for: .selected, barMetrics: .default)
        self.setBackgroundImage(image, for: .highlighted, barMetrics: .default)
        self.setDividerImage(image, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
    }
    
    override func awakeFromNib() {
        
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.bringSubviewToFront(underlineView)
    }
}

다음과 같이 segmentControl의 기본UI인 회색배경과 구분선을 삭제하고 underlineVIew를 추가하였습니다.

(# 처음부터 let image = UIImage를 녹색 언더바로 만든다면?)

이 과정에서 underlineView가 가려지는 현상이 발생해 self.bringSubviewToFront(underlineView)를 통해 underlineView를 앞쪽으로 가져왔습니다

다음으로 SegmentControl과 UIPageViewController가 세팅되어있다는 전제하에서 2가지를 처리해주어야 합니다.

  1. segmentControl 클릭시 언더바의 이동
  2. 뷰 스크롤시 segmentControl의 언더바 이동

먼저 segmentControl 클릭시 언더바의 이동 입니다.

@IBAction func clickSegment(_ sender: UnderlineSegmentedControl) {
        
        let segmentIndex = self.segmentControl.selectedSegmentIndex
        pageViewController.setViewcontrollersFromIndex(index: segmentIndex)
        let underlineFinalXPosition = (self.view.frame.width / CGFloat(segmentControl.numberOfSegments)) * CGFloat(segmentControl.selectedSegmentIndex)
        print(underlineFinalXPosition)
        UIView.animate(
            withDuration: 0.1,
            animations: {
                //restorationID를 통해 underlineView의 위치 지정
                if let underline = self.segmentControl.subviews.filter({$0.restorationIdentifier == "underbar"}).first{
                    underline.frame.origin.x = underlineFinalXPosition
                } else {
                    print("cannot find underlineView")
                }
                
            }
        )
    }

뷰 스크롤시 segmentControl의 언더바 이동

extension PageViewController: UIPageViewControllerDelegate, UIScrollViewDelegate {
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        if completed {
            completeHandler?(currentPageIndex)
        }
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let pageWidth = self.view.frame.width

        // 각각의 뷰의 위치를 변환
        for vc in vcArray {

            let pagePoint = vc.view.convert(CGPoint(), to: view)

            if pagePoint.x > CGFloat(0.0) && pagePoint.x < pageWidth {
                if let estimatePage = vcArray.firstIndex(where: {$0 == vc}) {

                    estimateOffSetX = CGFloat(estimatePage) * pageWidth - pagePoint.x

                    // 계산된 x값을 확인해보면 살짝씩 오차가 있음
                    print(estimateOffSetX)

                    if let underline = self.viewController.segmentControl.subviews.filter({$0.restorationIdentifier == "underbar"}).first{
                        underline.frame.origin.x = estimateOffSetX / 10
                    }
                }
            }
        }
    }
}

PageViewController에 UIScrollViewDelegate를 채택후 scrollViewDidScroll 메서드 내부에 다음과 같이 구현합니다 이때 estimateOffSetX값의 계산과 gesture로 인한 UI변경에 약간의 시간차로 인해 오차가 존재 합니다. 또한 segmentControl에서 정방향( ex. 월 → 수)으로는 잘 작동하지만 역방향(수 → 월)으로 클릭시 underlineView가 과도하게 움직이는 현상이 발생합니다 이 부분에 대해 서는 공부가 필요합니다.

결과화면

'ios > UI구현' 카테고리의 다른 글

[iOS, Swift] 네이버지도의 BottomSheet  (0) 2023.07.27
[iOS, Swift] AppStore layout만들어보기(UICollectionView, CompositionalLayout 활용)  (0) 2023.07.27
[iOS, Swift] Sticky Header(배달의민족, 당근마켓)  (0) 2023.07.27
[Swift] UICollectionView - Compositional Layout section의 동적인 높이 조절  (0) 2023.05.30
  1. 구현 아이디어
  2. 구현과정
  3. 결과화면
'ios/UI구현' 카테고리의 다른 글
  • [iOS, Swift] 네이버지도의 BottomSheet
  • [iOS, Swift] AppStore layout만들어보기(UICollectionView, CompositionalLayout 활용)
  • [iOS, Swift] Sticky Header(배달의민족, 당근마켓)
  • [Swift] UICollectionView - Compositional Layout section의 동적인 높이 조절
chobo5
chobo5
개발 기록
전체
오늘
어제
chobo5
chobo5's blog
chobo5
  • 분류 전체보기
    • ios
      • 문법
      • UIKit
      • UI구현
      • 기타
    • IT
    • Java
      • 기초
      • Spring
      • JPA
    • Algorithm
    • OOP
    • SQL

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

태그

  • getter
  • API
  • static
  • Swift
  • 프로퍼티
  • Lazy
  • setter
  • didset
  • restapi
  • property

최근 글

hELLO · Designed By 정상우.
chobo5
[iOS, Swift] 네이버 웹툰 메인 페이지(UIPageViewController, UISegnmentControl 활용)
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.