[ swift ] UITableViewDelegate 활용 예시

sonny·2024년 11월 22일
6

TIL

목록 보기
49/133
post-thumbnail

Delegate 패턴은 iOS 개발에서 매우 자주 사용되는 디자인 패턴 중 하나로,

UIKit에서도 주요한 역할을 한다고 한다.

UITableViewDelegate와 UICollectionViewDelegate는 Delegate 패턴의 대표적인 사례인데,

사용자와 인터페이스 간 상호작용을 처리하는 데 활용된다고 한다.


Delegate란 무엇인가?

Delegate는 일종의 대리인으로, 특정 객체의 행동을 다른 객체에 위임하는 디자인 패턴이다.

  • 테이블 뷰(혹은 컬렉션 뷰)는 자체적으로 다양한 기능을 가지고 있지만, 모든 동작을 내부에서 처리하기보다 "특정 이벤트 발생 시 그 처리를 다른 객체(대리인)에게 맡긴다"는 방식으로 구현된다.

테이블 뷰와 Delegate

테이블 뷰의 동작을 제어하기 위해 UITableViewDelegate 프로토콜이 존재한다.

  • 테이블 뷰는 자체적으로 데이터를 표시하거나 특정 동작을 처리할 수 있는 능력이 없다.
  • 대신, 개발자가 UITableViewDelegate 메서드를 구현하면 테이블 뷰의 동작 (예: 셀 선택, 셀 높이 설정 등)을 "대리"로 처리한다.

쉽게 말해, 테이블 뷰는 "무언가 이벤트가 발생하면 Delegate에게 알려주겠다"는 철학으로 설계된 것이다.

Delegate 패턴이 동작하는 과정

  1. 테이블 뷰에서 이벤트 발생
  • 사용자가 특정 셀을 탭함.
  1. Delegate에게 이벤트 전달
  • 테이블 뷰는 자신과 연결된 Delegate 객체를 호출한다.
  • didSelectRowAt indexPath 메서드 호출.
  1. Delegate에서 이벤트 처리
  • Delegate 객체(대부분 뷰 컨트롤러)가 원하는 동작을 정의하고 처리한다.
  • 해당 셀에 대한 세부 정보를 표시하는 화면으로 전환.

공부한 내용과 Delegate의 관계

어제 테이블 뷰와 관련해 작성한 메서드들

import UIKit

// Model: 테이블에 표시할 데이터
struct Movie {
    let title: String
    var year: Int
}

// ViewController: UITableViewController를 상속받아 Controller 역할을 수행
class MovieTableViewController: UITableViewController {
    var movies: [Movie] = [
        Movie(title: "킹콩", year: 2010),
        Movie(title: "해리포터", year: 2008),
        Movie(title: "마션", year: 2017),
        Movie(title: "부부의 세계", year: 2021),
        Movie(title: "킹콩", year: 2010),
    ]
    override func viewDidLoad() {
        super.viewDidLoad()
        // 테이블 뷰의 레이아웃, 스타일 설정
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MovieCell")
    }
    
    // 테이블 뷰의 셀을 설정하는 메서드
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return movies.count
    }
    // 각 셀의 내용을 설정하는 메서드
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath)
        
        let movie = movies[indexPath.row]
        cell.textLabel?.text = "\(movie.title)의 개봉연도는 \(movie.year) 입니다"
        
        return cell
    }
}

이 코드가 UITableViewDelegate와 관련 있는 이유를 말하자면,

이 코드는 UITableViewController를 상속받고 있다.

UITableViewController는 기본적으로 UITableViewDelegate와

UITableViewDataSource를 자동으로 채택하고 구현하는 구조를 제공하는데,

그러니 이미 UITableViewDelegate를 활용해 테이블 뷰의 동작을 정의할 준비가 되어 있다는 뜻.

이 코드에서 테이블 뷰의 동작(셀의 내용, 셀의 개수 등)은

tableView(_:numberOfRowsInSection:)과 tableView(_:cellForRowAt:) 메서드를 통해 정의되었다.

이 메서드들은 UITableViewDataSource 프로토콜의 일부지만,

테이블 뷰와 데이터를 주고받는 역할을 담당하는 점에서 Delegate와 밀접한 관계가 있다.


UITableViewDelegate 메서드 추가하기

현재 코드에는 UITableViewDelegate의 메서드가 구현되어 있지 않다.

이벤트를 처리하기위해 Delegate 메서드를 구현해야 해보자면

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let selectedMovie = movies[indexPath.row]
    print("\(selectedMovie.title)을 선택했습니다!")
}

Delegate와 코드의 관계 요약

현재 코드의 경우

  • UITableViewDataSource 메서드로 데이터를 표시.
  • UITableViewController가 이미 UITableViewDelegate를 포함하므로 필요 시 Delegate 메서드를 구현할 수 있음.

Delegate가 할 수 있는 일이라면

  • 셀이 선택되었을 때(didSelectRowAt) 동작 정의.
  • 셀의 높이 조정(heightForRowAt).
  • 헤더/푸터 내용 제공(viewForHeaderInSection). ⬇︎
  • viewForHeaderInSection은 헤더를 설정하는 메서드이고,
    viewForFooterInSection은 푸터를 설정하는 메서드다.
    이 메서드들을 사용해 기본 텍스트 대신 원하는 커스텀 뷰를 배치할 수 있다.

Delegate 패턴과 연결지어 생각해보자

Delegate는 "테이블 뷰의 일부 동작을 컨트롤러(ViewController)에게 위임"한다.

그러니까 테이블 뷰는 셀이 선택되었을 때 무슨 동작을 해야 할지 모른다는 것이다.

그래서 "셀 선택 이벤트를 위임받은 Delegate(뷰 컨트롤러)"가 처리를 하는 것.

이렇게 테이블 뷰와 Delegate는 협력하여 동작을 완성할 수 있다.

"Delegate가 전달하는 것"이란 무엇인가?

  • 테이블 뷰는 다음과 같은 정보를 Delegate에게 "전달"한다:
    1. 어떤 셀이 선택되었는지indexPath 전달.
    2. 셀의 높이를 어떻게 설정할지heightForRowAt 메서드 호출.
    3. 헤더/푸터를 어떻게 표시할지viewForHeaderInSection 메서드 호출.

테이블 뷰의 Delegate는 테이블 뷰와 상호작용하며 정보를 전달받아

개발자가 원하는 동작을 구현할 수 있도록 돕는 역할을 한다는 걸 알 수 있다.

이어서 UICollectionViewDelegate에 대해 알아보자.


UICollectionViewDelegate

UICollectionViewDelegateUICollectionView에서 발생하는 사용자 상호작용(이벤트)과 관련된 동작을 처리하기 위해 사용하는 프로토콜이다.

이 프로토콜을 구현하면 셀의 선택, 강조, 특정 동작에 대한 응답을 설정할 수 있고,

사용자의 인터랙션에 따라 동작을 커스터마이징할 수 있기 때문에 UICollectionView를 더 동적으로 사용할 수 있다고 한다.


UICollectionViewDelegate의 주요 역할

  1. 셀 선택 처리
  • 사용자가 특정 셀을 선택하거나 선택을 취소했을 때 호출되는 메서드를 구현한다.
  • 대표 메서드
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)
  1. 셀 강조 (Highlighting)
  • 사용자가 셀을 터치했을 때 강조 상태(highlighted)를 제어할 수 있다.
  • 대표 메서드
    func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool
    func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)
    func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)
  1. 포커스 관련 동작
  • 셀이 포커스를 받을 수 있는지와 관련 동작을 제어한다.
  • 대표 메서드
    func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool
    func collectionView(_ collectionView: UICollectionView, shouldUpdateFocusIn context: UICollectionViewFocusUpdateContext) -> Bool
  1. 컨텍스트 메뉴 관련 동작 (iOS 13 이상)
  • 사용자가 셀을 길게 눌렀을 때 컨텍스트 메뉴를 제공하도록 설정할 수 있다.
  • 대표 메서드:
    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?

UICollectionViewDelegate를 설정하는 방법

Delegate 설정: UICollectionViewDelegate를 채택한 객체를 delegate 속성에 연결해야 한다. 보통 ViewController가 이를 채택한다.

class MyViewController: UIViewController, UICollectionViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("Item selected at \(indexPath)")
    }
}

필요한 메서드 구현: 사용하고자 하는 동작에 맞게 메서드를 구현하면 된다.

UICollectionViewDelegate는 셀의 상호작용을 처리할 수 있는 도구라는 걸 알고있으면 되고,

선택이나 강조와 같은 이벤트를 필요에 따라 구현을 하면 될 것 같다.


정리하자면, 왜 Delegate 패턴을 쓰는가?

  1. 책임 분리
  • 테이블 뷰는 기본적인 UI와 데이터 관리를 담당하고, 구체적인 동작은 Delegate에게 위임한다.
  • 즉, 테이블 뷰는 재사용성확장성을 유지할 수 있다.
  1. 유연성 제공
  • Delegate 패턴을 사용하면 동일한 테이블 뷰라도 다른 Delegate 구현을 통해 다른 동작을 정의할 수 있다.
  1. 더 작은 코드베이스
  • 모든 동작을 테이블 뷰에서 처리하면 코드가 복잡해지지만, Delegate 패턴으로 필요한 동작만 구현하면 된다.

비유로 이해해보자

음...

오늘 공부를 통해 Delegate가 되게 중요한 기능이구나 라는걸 깨달았다.

테이블 뷰와 컬렉션 뷰가 단순히 데이터를 보여주는 것을 넘어

사용자와 상호작용하는 인터페이스로 동작할 수 있도록 만드는 핵심이

바로 Delegate라는 사실도 알게 되었고,

UITableViewDelegate와 UICollectionViewDelegate를 통해

iOS 개발자는 사용자 경험을 세밀하게 제어할 수 있다는 점에서 UIKit이 유연한 존재임을 알게 되었다.

Delegate 패턴이 처음에는 단순히 "메서드를 구현하는 규약"처럼 보였는데,

사실 실질적으로 어떻게 다른 객체와 연결되고 데이터를 전달하는지 이해하는 데 시간이 많이 걸렸다.

지금도 이걸 다 쓰고 한번 더 읽어보려한다.

profile
iOS 좋아. swift 좋아.

7개의 댓글

comment-user-thumbnail
2024년 11월 22일

오 문자 썸네일 신기해요
다음엔 NotificationCenter해주세요 작가님
이집 공부 잘 되네

1개의 답글
comment-user-thumbnail
2024년 11월 22일

캬아아 delegate 모를 때마다 여기 와야겠다

1개의 답글
comment-user-thumbnail
2024년 11월 24일

오... 여기 작가님 블로그 맛집이라는 소문이 사실이네요 ㅎㄷㄷ...

1개의 답글
comment-user-thumbnail
2024년 11월 29일

델리게이트 이해하러 왔는데 쉽지않네요

답글 달기

관련 채용 정보