[TIL] UITableView

박주하·2025년 6월 19일

UITableView


  • 세로 스크롤(가로 ❌)을 통해 목록을 표시해주는 뷰 (ex: 연락처 목록, 채팅 목록 등)
  • UITableView: 화면에 보일 셀만 그림
  • dataSource: 실제 셀의 데이터를 알려줌
  • delegate: 사용자 인터랙션(클릭 등)을 처리함
  • 항상 Delegate + DataSource가 필요
  • 셀을 재사용하여 스크롤이 빠르고 메모리 사용이 적음

구성요소설명
Cell목록에 보이는 각각의 항목 (UITableViewCell)
SectionCell들을 묶는 구간 (옵션)
Header/Footer섹션마다 붙는 설명 뷰 (옵션)
DataSource어떤 Cell을 보여줄지 알려주는 데이터 담당
Delegate셀 클릭, 높이 설정 등 동작 담당

필수 메서드들


1. UITableViewDataSource (데이터 제공자)

extension MyTableViewController: UITableViewDataSource {
	// 셀의 개수
	func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {...}
    
    // 셀을 어떻게 만들지
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {...}
}

2. UITableViewDelegate (동작 제어)

extension MyTableViewController: UITableViewDelegate {
	// 셀을 눌렀을 때 동작
	func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {...}
}
	// 셀의 높이
	func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat

Header 추가


1. 전체 테이블 상단 헤더

  • tableView.tableHeaderView: 테이블 맨 위의 전체 헤더 (1개만 가능)
// 테이블 전체의 상단에 고정되는 헤더
let headerView = UILabel()
headerView.text = "🍓 오늘의 과일"
headerView.textAlignment = .center
headerView.font = .systemFont(ofSize: 20, weight: .bold)
headerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)

tableView.tableHeaderView = headerView

2. 각 섹션별 헤더

기본 텍스트 헤더

  • UITableViewDataSource의 메서드
  • titleForHeaderInSection: 섹션 텍스트 헤더
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return "과일 목록"
}

커스텀 뷰 헤더

  • UITableViewDelegate의 메서드
  • viewForHeaderInSection: 섹션 커스텀 헤더 (UILabel, UIView 등 사용 가능)
  • heightForHeaderInSection: 섹션 헤더 높이 (기본값 0이면 안 보임)
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let label = UILabel()
    label.text = "과일 목록"
    label.backgroundColor = .systemGroupedBackground
    label.font = .boldSystemFont(ofSize: 16)
    label.textColor = .darkGray
    label.textAlignment = .left
    label.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: 44)
    return label
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 44
}

셀 삭제 스와이프 추가


  • UITableViewDelegate의 메서드
  • canEditRowAt: 셀 편집(삭제, 이동, 삽입 등) 가능 여부를 설정
  • trailingSwipeActionsConfigurationForRowAt: 커스텀 스와이프(오른쪽 → 왼쪽) 버튼을 설정 (iOS 11 이상에서 사용 가능)
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true  // 모든 셀에서 편집(삭제)을 허용
}

// 커스텀 스와이프 버튼 설정
func tableView(_ tableView: UITableView,
               trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
-> UISwipeActionsConfiguration? {
    
    let delete = UIContextualAction(style: .destructive, title: "삭제") { _, _, completion in
        self.fruits.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)
        completion(true)
    }

    return UISwipeActionsConfiguration(actions: [delete])
}

전체 코드


1. UIViewController

import UIKit

class MyTableViewController: UIViewController {

    // 테이블 뷰 인스턴스 생성
    let tableView = UITableView(frame: .zero, style: .insetGrouped)
    
    // 보여줄 데이터 (과일 목록)
    var fruits = ["🍎 사과", "🍌 바나나", "🍇 포도"]

    override func viewDidLoad() {
        super.viewDidLoad()

        // 전체 테이블 상단에 붙는 헤더뷰 설정
        let headerView = UILabel()
        headerView.text = "🍓 오늘의 과일"  // 헤더에 표시할 텍스트
        headerView.textAlignment = .center
        headerView.font = .systemFont(ofSize: 20, weight: .bold)
        headerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)

        // 테이블 뷰의 tableHeaderView에 할당
        tableView.tableHeaderView = headerView

        // 테이블 뷰를 뷰에 추가하고 전체 화면에 맞게 배치
        view.addSubview(tableView)
        tableView.frame = view.bounds

        // 데이터 제공자 설정 (UITableViewDataSource 프로토콜)
        tableView.dataSource = self

        // 사용자 인터랙션 처리자 설정 (UITableViewDelegate 프로토콜)
        tableView.delegate = self

        // 셀의 높이를 자동으로 계산하도록 설정
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 44  // 셀 높이를 예측할 기본 값

        // 셀 재사용을 위한 등록 (identifier "cell")
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    }  
}

2. UITableViewDataSource

// 데이터 관련 메서드
extension MyTableViewController: UITableViewDataSource {

    // 섹션별 셀 개수 설정
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fruits.count
    }

    // 셀 구성 메서드 (각 셀에 어떤 내용을 넣을지 정의)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // 셀 재사용 (메모리 절약)
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

        // 셀의 텍스트 라벨에 과일 이름 넣기
        cell.textLabel?.text = fruits[indexPath.row]
        return cell
    }
}

3. UITableViewDelegate

// 사용자 인터랙션 및 뷰 커스터마이징
extension MyTableViewController: UITableViewDelegate {

    // 섹션 헤더에 보여줄 커스텀 뷰 설정
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let label = UILabel()
        label.text = "과일 목록"  // 섹션 이름
        label.backgroundColor = .systemGroupedBackground
        label.font = .boldSystemFont(ofSize: 16)
        label.textColor = .darkGray
        label.textAlignment = .left
        label.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: 44)
        return label
    }

    // 섹션 헤더 높이 지정
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 44
    }
    
    // 셀 클릭 시 동작 처리
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 어떤 과일을 선택했는지 출력
        print("선택한 과일: \(fruits[indexPath.row])")

        // 선택 효과 해제 (선택 시 회색 배경 제거)
        tableView.deselectRow(at: indexPath, animated: true)
    }
    
    // 셀 편집 가능 여부 설정
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true  // 모든 셀에서 편집(삭제)을 허용
    }

    // 커스텀 스와이프 버튼 설정
    func tableView(_ tableView: UITableView,
                   trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
    -> UISwipeActionsConfiguration? {

        // 삭제 액션 정의
        let delete = UIContextualAction(style: .destructive, title: "삭제") { _, _, completion in
            // 데이터 원본에서 해당 항목 삭제
            self.fruits.remove(at: indexPath.row)

            // 테이블 뷰에서 해당 셀 삭제 애니메이션 적용
            tableView.deleteRows(at: [indexPath], with: .automatic)

            // 삭제 완료 처리 (필수)
            completion(true)
        }

        // 삭제 액션을 포함한 스와이프 구성 반환
        return UISwipeActionsConfiguration(actions: [delete])
    }
}

4. 결과 화면

0개의 댓글