[UITableView] section별로 cell을 열고 닫을 수 있도록 만들기

2021. 9. 7. 22:58UIKit, SwiftUI, H.I.G

앱잼을 하면서 구현하기 제법 어려웠던 화면이 있었다.

같이 여행을 가는 구성원들끼리 성향 테스트 결과를 각 문항마다 확인할 수 있는 뷰였는데,
테이블 뷰를 사용하고 테이블 뷰 헤더 내에 있는 버튼을 클릭하면, Cell이 확장됬다가 접혔다 하는 기능을 필요로 하는 화면이었다.

사실 스스로 챌린지를 해보고 싶어서 이 화면을 맡겠다고는 했지만, 생각보다 issue가 많은 어려운 화면이라 같은 아요 선배님들의 도움을 많이 받아 완성할 수 있었다 ^_^

왼쪽이 접혀있을 때 모습, 오른쪽이 펼쳐져있을 때 모습이다.

우선 이 뷰를 어떻게 구성할 지부터 생각이 필요했다. 다양한 방법이 있을 수 있겠지만, 나는 아래와 같은 방법을 생각해보았다.

우선, 크게는 TableView를 사용하면서 "이번 여행에서 우리는?"이라 써있는 상단 고정 뷰를 TableView의 header로 설정했다.
아래에는 각 질문이 하나의 section으로 구성을 해서 항상 노출되도록 되어있는 질문이 각 section의 header로,
접혔다 펼쳐졌다 하는 선택지는 section의 cell로 구성하는 방법대로 생각했다.

이 형태로 구성한다면, 각 섹션마다 존재하는 헤더가 사용자의 터치를 인식할 때 row가 드러나게 되는 모습을 생각할 수 있을 것이다.

뷰의 세부요소들은 이렇게 구성되어 있다.

뷰 구성이 끝났으니, 이제 뷰컨부터 차근차근 "코드"를 살펴보자.

우선 예전 글에서도 다뤘듯이, 테이블 뷰에서는 필연적으로 cell reuse 이슈가 생길 수밖에 없다. (예전 글이 궁금하다면? 아래를 클릭클릭 ><)

 

[UITableView] 테이블뷰에서 발생했던 Cell reuse 문제와 해결방법

아이폰에서 가장 많이 사용되는 뷰가 무엇이냐 묻는다면 TableView와 CollectionView라고 답할 수 있을 것이다. 이 두 개만 잘 배워놔도 대부분의 뷰를 구현할 수 있을 만큼 테이블뷰와 컬렉션뷰는 iOS

mini-min-dev.tistory.com

 

이번 화면도 예외가 아니었는데, 아래처럼 isOpen이라는 Bool값을 담을 수 있는 배열을 선언했다.

질문이 10개였으므로 값도 10개를 담았으며, 처음 상태를 접혀있는 상태로 정했기에 false를 담았다.

(true: section row가 펼쳐져 있는 상태 / false: section row가 접힌 상태)

그리고 지난번과 똑같이 데이터 상태를 뷰에다가 전달해주고

(데이터 상태: isOpen[section], 해당 섹션이 열린 상태인지 아닌지)

delegate pattern을 활용해서 데이터를 가져오면 된다.

(사실, 이 부분은 버튼과 섹션 클릭에 있어서 기능 구현이 꼬인 부분이다.) (더 말끔한 코드로...공부 이후 업데이트해보겠다..)

아무튼, 뷰컨에서는 이 해당 프로토콜을 채택해서, 필요한 내용을 구현해주었다.

해당 sectionIndex(위치)에 따라서, isOpen을 조건으로 나누어 insertRows, deleteRows를 활용하는 방식으로 자세한 기능을 구현했다 ^__^

Section header를 구현한 아래 QuestionCell 부분을 더 자세하게 살펴보자.

UITableViewHeaderFooterView를 상속받아서 sectionHeaderView를 구현했다.
UITabGestureRecognizer를 추가해준 것은 사용자가 이 뷰를 클릭했을 때, 동작을 할 수 있게 하기 위해서다.

아래처럼 contentView에 addGestureRecognizer를 해주면,
sectionHeaderView의 어느 부분을 클릭해도 TapGestureRecognizer가 발동하게 된다.

갑자기 뜬금없이 override func draw라는 부분이 나와서 당황했을 수도 있겠다.

이 부분은 그냥 UITableViewHeaderFooterView가 init(생성자) 키워드를 표시하는 방법이다.

추가로, 우리는 isOpen의 상태에 따라 버튼 이미지도 바꿔줘야 한다.

닫혀 있을 때는 화살표가 아래로 향하도록, 열려 있을 때는 화살표가 위로 향하도록 해야 하는데, 그 부분을 구현한 곳이 아래 코드이다.

잠깐, didSet이라는 생소한 명령어가 나왔다.

didSet은 간단하게 말해, 프로퍼티의 값이 변경된 직후에 호출되는 부분이다.
(did, will이 나오는 뷰컨의 생명주기와 비슷하다고 생각하면 됨!)

그렇다면, willSet은?
프로퍼티의 값이 변경되기 직전에 호출되는 부분이 되겠다.

그러므로 위 코드에서는 isOpened라는 Bool값이 false에서 true로 바뀌게 될 때,
그 값에 따라 이미지를 "iconRightUp"을 사용할지, "iconRightDown"을 사용할지 선택하는 부분인 것이다.

이 모든 과정이 끝나면, 구현 가능해진다 ^__^
사실 조금 급하고 넘어간 개념들이 많은데, 뒤에 iOS 글들을 작성하면서 수정해가도록 하겠다.

오늘은 여기까지✌🏻

 

 

Reference

 

[iOS]TableView Expand (by touch on section) - TableView section별로 접기/펴기

[tableVeiw 접기/펼치기]오늘 다를 주제는 tableView의 섹션별로 표시된 cell들을 섹션을 클릭하여 접었다 폈다하는 방법에 관한 것입니다. 여러가지 방법이 있는데요. 이번 포스트에서는 2가지 방법에

velog.io