개발/Swift

Tableview와 스크롤을 다루는 방법

덤벨로퍼 2023. 5. 31. 18:17

Modern Collection View 와 MVVM 패턴 가이드

 

[iOS] Swift Modern Collection View & MVVM 패턴 가이드 | 덤벨로퍼 - 인프런

덤벨로퍼 | MVVM 패턴과 Modern Collection View를 사용해 네트워킹을 구현하고, 다양하고 동적인 Collection View를 자유자재로 다룰 수 있게 됩니다., Swift iOS UI, 제대로 다루는 핵심 기술! 📲 iOS Swift 레이

www.inflearn.com

 


Tableview의 길이가 길어져서 디바이스의 높이보다 길어지는순간

스크롤을 사용할수 있고 그덕에 긴 컨텐츠를 한 페이지에 보여줄수 있다.

그래서 table view의 사용목적 자체가 스크롤과 연관이 깊다.

이로 인해 스크롤과 테이블뷰 간의 관계를 잘 다루는게 유용할때가 많다.

 

어느날 이런 요구 사항을 구현해야했다.

  1. 테이블뷰가 있고 별개로 가장 하단에 고정 버튼이 있다.
  2. 스크롤의 높이에따라 버튼의 Title이 바뀐다. (스크롤이 가장 하단에 위치할때 , 그외)
  3. 하단 버튼을 클릭했을때 스크롤의 높이에따라 다른 작동을 한다. (스크롤이 가장 하단에 위치할때 , 그외)
    • 가장 밑으로 스크롤 이동하기
    • 다른 동작

이를 구현 하기 위해 구현해야할 함수는 다음과 같았다.

  1. 스크롤이 움직일때 지금 스크롤의 위치를 감지할 기능
  2. 가장 하단의 offset을 구하는 함수
  3. 스크롤의 위치를 가장 하단으로 이동할 기능
  4. 버튼 클릭했을때 스크롤에 위치에 다라 다르게 행동

스크롤의 위치를 감지

tableView.rx.contentOffset
    .skip(1)
    .bind { [weak self] point in
       print(point.y)
    }.disposed(by: disposeBag)

rxcocoa 를 사용하면 아주 감사하게도 tableview contentOffset 를 제공해준다.

Tableview의 가장 하단의 y offset을 구하는 함수

private func getBottomYOffset() -> CGFloat {
    let contentSize = tableView.contentSize.height
    let frameSize = tableView.frame.size.height
    return contentSize - frameSize
}

content 의 길이와 frame 의 길이를 빼서 구할 수 있다.

스크롤이 가장 위에 위치했다면 Y offset 의 값은 0이다 (디바이스 기준 가장 위 지점이라 생각하면 됨)

스크롤 이 가장 하단에 위치한것은 테이블 뷰의 컨텐츠 높이에서 디바이스의 크기를 뺀 값이다 테이블 뷰의 frame 크기는 디바이스 크기와 같다 (크기 제약조건을 equal to superview로 지정했다면)

 

ContentSize FrameSize 는 아래와 같다.

스크롤의 위치를 가장 하단으로 이동

private func scrollToBottom() {
      tableView.setContentOffset(.init(x: 0, y: getBottomYOffset()), animated: true)
  }

위에서 구현한 함수를 통해 쉽게 이동시킬수 있다.

더불어 지금 위치가 바닥인지 아닌지 를 위에서 구현한 함수들을 통해 구현할수 있다.

private func checkScrollIsBottom(yOffset: CGFloat) -> Bool {
        return yOffset >= getBottomYOffset() - 20
    }

tableView.rx.contentOffset
    .skip(1)
    .bind { [weak self] point in
        guard let s = self else { return }
       
        if s.checkScrollIsBottom(yOffset: point.y) {
	          //Do SomeThing
        } else {
					 //Do SomeThing
        }
    }.disposed(by: disposeBag)

버튼 클릭시 스크롤에 위치에 따라 다르게 행동

이것또한 rx를 사용해 쉽게 구현할수 있다.

bottomButton.rx.tap.withLatestFrom(confirmTableView.rx.contentOffset)
      .bind { [weak self] point in
          guard let s = self else {return }
          if s.checkScrollIsBottom(yOffset: point.y) {
              //Do SomeThing
          } else {
              s.scrollToBottom()
          }
      }.disposed(by: disposeBag)