iOS) UITableView - 기초(2)

seuhong·2022년 8월 23일
0
post-thumbnail

Delegate와 DataSource

  • delegate와 datasource는 테이블뷰 구현에 꼭 필요한 요소!
    -> 나중에 공부할 컬렉션뷰(collectionView)에서도 마찬가지

TableViewDelegate

  • 테이블뷰 delegate 객체는 UITableViewDelegate 프로토콜을 준수합니다.
  • 테이블뷰 delegate는 테이블뷰의 모양과 동작을 관리하며 MVC 디자인패턴 중, Controller와 관련이 있습니다.
  • delegate는 테이블뷰의 시각적인 부분의 수정과 행의 선택관리, 액세서리뷰 지원 그리고 테이블뷰의 개별 행 편집을 도와줍니다.
  • UITableViewDelegate 프로토콜의 주요 메소드는 아래와 같으며 이 중 필수로 구현해야하는 메서드는 없습니다.
// 지정된 행이 선택되었음을 알리는 메서드
func tableView(UITableView, didSelectRowAt: IndexPath)

// 지정된 행의 선택이 해제되었음을 알리는 메서드
func tableView(UITableView, didDeselectRowAt: IndexPath)

// 특정 위치 행의 높이를 묻는 메서드
func tableView(UITableView, heightForRowAt: IndexPath)

// 특정 위치 행의 들여쓰기 수준을 묻는 메서드
func tableView(UITableView, indentationLevelForRowAt: IndexPath)

// 특정 섹션의 헤더뷰 또는 푸터뷰를 요청하는 메서드
func tableView(UITableView, viewForHeaderInSection: Int)
func tableView(UITableView, viewForFooterInSection: Int)

// 특정 섹션의 헤더뷰 또는 푸터뷰의 높이를 물어보는 메서드
func tableView(UITableView, heightForHeaderInSection: Int)
func tableView(UITableView, heightForFooterInSection: Int)

// 테이블뷰가 편집모드에 들어갔음을 알리는 메서드
func tableView(UITableView, willBeginEditingRowAt: IndexPath)

// 테이블뷰가 편집모드에서 빠져나왔음을 알리는 메서드
func tableView(UITableView, didEndEditingRowAt: IndexPath?)

TableViewDataSource

  • 테이블뷰 datasource 객체는 UITableViewDataSource 프로토콜을 준수합니다.
  • datasource는 테이블뷰를 생성하고 수정하는데 필요한 정보를 테이블뷰 객체에 제공합니다.
  • datasource는 데이터 모델의 delegate로, 테이블뷰의 시각적 모양에 대한 최소한의 정보를 제공합니다.
  • 이에 MVC 디자인 패턴 중, Model과 관련이 있습니다.
  • UITableView 객체에 섹션의 수행의 수를 알려주며, 행의 삽입, 삭제 및 재정렬을 하는 기능을 선택적으로 구현할 수 있습니다.
  • @required 로 선언된 두 가지 메서드는 UITableViewDataSource 프로토콜을 준수한 타입에 필수로 구현해야 합니다.
@required 
// 특정 위치에 표시할 셀을 요청하는 메서드
func tableView(UITableView, cellForRowAt: IndexPath) 

// 각 섹션에 표시할 행의 개수를 묻는 메서드
func tableView(UITableView, numberOfRowsInSection: Int)

@optional
// 테이블뷰의 총 섹션 개수를 묻는 메서드
func numberOfSections(in: UITableView)

// 특정 섹션의 헤더 혹은 푸터 타이틀을 묻는 메서드
func tableView(UITableView, titleForHeaderInSection: Int)
func tableView(UITableView, titleForFooterInSection: Int)

// 특정 위치의 행을 삭제 또는 추가 요청하는 메서드
func tableView(UITableView, commit: UITableViewCellEditingStyle, forRowAt: IndexPath)

// 특정 위치의 행이 편집 가능한지 묻는 메서드
func tableView(UITableView, canEditRowAt: IndexPath)

// 특정 위치의 행을 재정렬 할 수 있는지 묻는 메서드
func tableView(UITableView, canMoveRowAt: IndexPath)

// 특정 위치의 행을 다른 위치로 옮기는 메서드
func tableView(UITableView, moveRowAt: IndexPath, to: IndexPath)
  • 전체적인 테이블뷰의 Delegate와 DataSource의 관계를 표현하면 아래의 그림과 같습니다.

테이블뷰 만들기

  • UIViewController에 UITableView와 UITableViewCell을 서브뷰로 넣어 만들어 볼 것임
  • 우선 프로젝트를 생성 후 다음과 같이 Storyboard에 UIViewController 위에 UITableView를 서브뷰로 추가합니다.

  • 그 다음 UITableView 안에 UITableViewCell을 추가합니다.

  • 추가된 cell의 inspector에 들어가서 identifier를 설정합니다. (필수)

  • 이제 UIViewController의 swift 파일로 가서 아래와 같이 순차적으로 실행합니다.
  • class에 UITableDelegate, UITableViewDataSource 프로토콜을 상속받습니다.
  • tableView 라는 이름의 UITableView 프로퍼티를 선언 후, 이전에 만들었던 storyboard의 테이블뷰와 연결합니다.(필수)
  • viewDidLoad()에서 delegate와 datasource를 self로 연결합니다.
  • tableArray 는 테이블뷰에서 보여줄 문자들을 담은 배열입니다.

이러면 기초작업이 끝났고, 다음과 같은 에러가 나게됩니다.

그 이유는 UITableViewDataSource의 필수(required) 메소드를 선언하지 않았기 때문!
그래서 클래스에 아래 두 메소드를 선언해야 합니다.

이제 위의 두 함수를 채워볼 것입니다. 우리는 "A, B, C, D, E" 5개의 문자열을 테이블뷰에 보여줄 것입니다. 그래서 보여줄 행의 개수를 담당하는 numberOfRowsInSections 메소드에 다음과 같이 배열의 개수를 반환해줍니다.

cellForRow 메소드는 indexPath에 어떤 cell이 들어갈 것인지를 설정하는 메소드.
그래서 반환타입이 UITableViewCell 이고, 함수 안에서 UITableViewCell 을 생성하여 커스텀한 후 그 cell을 반환하여 해당 셀이 특정 행에 적용되어야 합니다.

그래서 위와 같이 cell을 생성합니다.
첫 줄을 보면 gurard else {} 문이 보이는데, 이유는 아래와 같이 dequeueReusableCell의 반환값이 UITableViewCell? 인 optional 값이기 때문입니다.

dequeueReusableCell

  • 재사용 가능한 셀을 큐에서 빼내는 함수입니다.
    말 그대로 실제로 재사용 가능한 큐에서 특정 identifier의 셀을 가져오는 메소드입니다.

  • iOS 기기는 한정된 메모리를 가지고 애플리케이션을 구동합니다. 그래서 화면에 표시할 수 있는 뷰의 개수는 한정되어 있지만, 표현해야 하는 데이터가 많은 경우 반복된 뷰를 생성하기보다 뷰를 재사용하는것이 좋습니다. 사용할 수 있는 메모리가 작기 때문에 데이터의 양만큼 많은 뷰를 생성하는 것은 메모리를 많이 낭비할 수 밖에 없기 때문입니다.

  • 그래서 다음과 같은 원리를 이용해 UITableViewCell 이나 UICollectionViewCell 을 재사용 합니다.

  1. 테이블뷰 및 컬렉션뷰에서 셀을 표시하기 위해 datasource에 뷰(cell) 인스턴스를 요청.
  2. datasource는 요청마다 새로운 cell을 만드는 대신에 재사용 큐(Reuss Queue)에 재사용을 위해 대기하고 있는 셀이 있는지 확인 후 있으면 그 셀에 새로운 데이터를 설정하고, 없으면 새로운 셀을 생성.
  3. 테이블뷰 및 컬렉션뷰는 datasource가 cell을 반환하면 화면에 표시.
  4. 사용자가 스크롤을 하게되면 일부 셀들이 화면 밖으로 사라지면서 다시 재사용 큐에 들어감.
  5. 위의 1~4번 과정이 계속 반복됨.

이와 같은 이유와 원리로 dequeReusableCell 메소드를 사용하는 것입니다. 그리고 파라미터로 들어가는 identifier 는 위의 storyboard의 UITableViewCell에 등록한 identifier 입니다. 이 identifier로 storyboard에 생성한 셀을 구분합니다.

이렇게 cell 변수에 tableviewCell identifier를 가진 테이블뷰 셀이 담긴다면 이 후 cell의 textLabel의 text를 알파벳으로 바꿔야함!
지금의 함수의 매개변수 indexPath에는 현재 행의 정보를 가지고 있으므로 indexPath.row에는 0부터 1까지 증가하는 index 를 가지고 있게 됩니다.

cell.textLabel?.text = tableArray[indexPath.row]

1 행에 "A", 2 행에 "B" ... 와 같이 테이블뷰에 띄워야 하므로 indexPath.row 또한 0부터 시작하며 tableArray의 index 또한 0부터 시작하기 때문에 위와 같은 코드를 입력하면 첫 행부터 tableArray의 데이터가 순차적으로 넣어지게 됩니다. tableArray의 데이터는 5개이므로 indexPath.row가 5인 6번째 cell부터는 어떤 정보도 들어가지 않게 됩니다.
indexPath의 row를 참조하는 이유는 print(indexPath) 출력을 해보면 알 수 있습니다.

이처럼 indexPath는 두 개의 정보를 담고 있습니다. 첫 0은 테이블뷰의 section의 정보이고 두 번째 숫자는 section의 행에 대한 정보입니다. indexPath.row 를 사용하여 두 번째 숫자를 참조할 수 있게됩니다.
이제 시뮬레이터를 실행하면?

이렇게 정상적으로 테이블뷰 튜토리얼을 완성하게 됩니다.

profile
완씨의 개발기록

0개의 댓글