반응형
안녕하세요🐶
빈지식 채우기의 비니🙋🏻♂️입니다.
오늘의 포스팅은 펼침이 가능한 UITableView 만들기입니다.
흔히들 Expandable UITableView 라고도 불리지요~
실무에서 해당 기능을 사용을 하게 되었고~ 정리를 해보았습니다.
Full Code 는 맨 하단에 Github 링크 올려놨으니 참고 부탁드립니다 ㅎㅎ
1. 프로젝트 구조
프로젝트 구조는 다음과 같습니다.
- 초록색 : Dummy Data를 위한 데이터 소스
- 빨간색 : 화면 출력을 위한 ViewController 소스
2. 소스
간단한 프로젝트 구조를 알아보았으니 작성된 소스에 대해 알아보도록 하겠습니다.
2-1. StoryBoard
- 간단히 TableView만 생성 후 ViewController에 연결합니다.
2-2. Model
class Comment { // 클릭이 가능한 Header에 들어갈 데이터
var commentId: Int
var commentText: String
var replies: [Reply]
init(commentId: Int, commentText: String, replies: [Reply]) {
self.commentId = commentId // 순서를 위한 ID
self.commentText = commentText // Title Text
self.replies = replies // Reply 배열 참조
}
}
class Reply { // 펼침이 되는 Cell에 들어갈 데이터
var replyId: Int
var replyText: String
init(replyId: Int, replyText: String) {
self.replyId = replyId // 순서를 위한 ID
self.replyText = replyText // Title Text
}
}
- TableView에 들어갈 데이터 구조를 작성합니다.
2-3. ViewController
2-3-1. 뷰 설정 및 Dummy Data 생성
class TableViewController: UIViewController {
@IBOutlet weak var tableView: UITableView! // 스토리보드 TableView 연결
var comments: [Comment] = [Comment]() // Dummy Data
var hiddenSections = Set<Int>() // 숨겨진 Section을 저장할 변수 선언
override func viewDidLoad() {
super.viewDidLoad()
initView() // 기초적인 View 설정
}
func initView() {
// TableView Delegate 및 DataSource 연결
tableView.delegate = self
tableView.dataSource = self
// 테이블뷰 라인 없애기
tableView.separatorStyle = .none
comments = getDummyComments(with: 3) // Dummy Data 생성
}
func getDummyComments(with count: Int) -> [Comment] {
var comments = [Comment]()
for i in 1...count {
comments.append(Comment(commentId: i,
commentText: "Comment \(i)",
replies: getDummyReplies(with: i)))
}
return comments
}
func getDummyReplies(with count: Int) -> [Reply] {
var replies = [Reply]()
for i in 1...count {
replies.append(Reply(replyId: i, replyText: "Reply \(i)"))
}
return replies
}
여기서 잠깐! 왜 숨겨진 Section 정보를 Set(집합)에 저장할까?
- 어떤 Section이 있다는 정보는 순서 중요 X
- Set은 중복되는 요소가 없기 때문에 그로 인한 실수 방지
- Hashable 프로토콜을 채택한 타입을 사용, 빠르게 탐색 가능
2-3-2. TableView DataSource와 Delegate 메서드 구현
extension TableViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return comments.count // Section 개수 설정
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.hiddenSections.contains(section) {
return 0 // 섹션이 hidden이므로 행을 노출 X.
}
return comments[section].replies.count // 가진 데이터의 개수만큼 노출.
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell() // TableView Cell 생성
// Dummy Data 삽입
cell.textLabel?.text = (comments[indexPath.section].replies[indexPath.row]).replyText
return cell
}
}
extension TableViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerText = UILabel() // Section 에 사용될 UILabel 생성
headerText.textColor = .lightGray
headerText.adjustsFontSizeToFitWidth = true
headerText.textAlignment = .left
headerText.text = comments[section].commentText // Dummy Data의 Comment 데이터 삽입
headerText.tag = section // 숨김 처리를 위한 Tag 설정
// Section 선택 시 Tap 이벤트 설정
let tap = UITapGestureRecognizer(target: self,
action: #selector(self.hideSection(sender:)))
headerText.isUserInteractionEnabled = true
headerText.addGestureRecognizer(tap)
return headerText
}
}
- DataSource 와 Delegate 함수 선언 및 작업
- Section 부분 UILabel로 설정
@objc
private func hideSection(sender: UITapGestureRecognizer) {
// section의 tag 정보를 가져와서 어느 섹션인지 구분한다.
let section = sender.view!.tag
// 특정 섹션에 속한 행들의 IndexPath들을 리턴하는 메서드
func indexPathsForSection() -> [IndexPath] {
var indexPaths = [IndexPath]()
for row in 0..<comments[section].replies.count {
indexPaths.append(IndexPath(row: row,
section: section))
}
return indexPaths
}
// 가져온 section이 원래 감춰져 있었다면
if self.hiddenSections.contains(section) {
// section을 다시 노출시킨다.
self.hiddenSections.remove(section)
self.tableView.insertRows(at: indexPathsForSection(), with: .fade)
self.tableView.scrollToRow(at: IndexPath(row: comments[section].replies.count - 1,
section: section),
at: UITableView.ScrollPosition.bottom, animated: true)
} else {
// section이 원래 노출되어 있었다면 행들을 감춘다.
self.hiddenSections.insert(section)
self.tableView.deleteRows(at: indexPathsForSection(), with: .fade)
}
}
- Section 클릭 시 호출 될 hideSection 구현
3. 실행
- 위와 같은 작업이 이루어 지면 다음과 같은 실행 결과를 얻을 수 있다.
4. 전체 소스
이상으로 Expandable UITableView 만들기 포스팅을 마치겠습니다.
틀린 부분이나 궁금한 사항은 댓글 남겨주세요~
참고
반응형
'iOS📱 > Swift' 카테고리의 다른 글
[Swift] Json Parsing ( JsonSerialization vs Codable ) (0) | 2024.02.23 |
---|---|
[ Swift ] URLComponents 알아보기 (0) | 2023.06.15 |
[ RxSwift ] Observable & Subscribe (0) | 2023.05.09 |
[ Swift ] 일급 객체 ( First-Class Object ) (2) | 2023.01.26 |
[ Swift ] 제네릭 ( Generic ) (0) | 2023.01.10 |