 Apple Lover Developer & Artist

영속적인 디자인에 현대의 공감을 채워넣는 공방입니다

 Apple/Stanford iOS Programming (UIKit)

Lecture 13 : TableView & Collection View

singularis7 2021. 12. 3. 12:06
반응형

Overview

UITableView와 UICollectionView 에 대해서 다룬다!
  • iOS 에서 모두 이미 본 개념이기 때문에 무슨 역할을 하는지 이미 알고있을 수 있다.
  • UIScrollView의 Subclass이다.
  • 범위가 지정되지 않은 양의 정보에 접근하기위해 사용된다.
  • 테이블 뷰는 긴 리스트의 형태로 정보를 보여준다.
  • 컬렉션 뷰는 Configurable 한 (구성가능한) 방식으로 제공되며 거의 모든 2차원 방식을 제공한다.
  • 텍스트가 흐르는 것처럼 2차원 format 으로 보여주기 때문에 Flow Layout 이라고 부르기도 한다.
  • 텍스트가 흐른다는 표현은 왼쪽에서 오른쪽으로 나열되다가 공간이 부족하면 다음 줄로 이동한다.
  • 이것이 바로 컬렉션 뷰가 기본적으로 배치되는 방식이다.
  • 사용자 정의하는 방식에 관해서는 다루지 않는다!
  • 하지만 개발자가 스스로 자신만의 레이아웃을 작성할 수 있는 방법이 있다는 것을 알고있어야한다!
  • 테이블 뷰와 컬렉션 뷰가 함께 설명되는 이유는 프로그래밍 인터페이스가 서로 매우 유사하기 때문이다.
  • 그냥 눈에 보이는게 아래로 나열되는가 text 처럼 flow 되는가의 차이이다.

UITableView

  • 가장 단순한 리스트 모양으로 보여질 수 있다.
  • 섹션으로 나눌 수도 있다.

  • 별도의 구현없이도 간단한 보조적인 정보를 표현할 수 있다! 여러가지 방법이 존재한다.
  • Subtitle Style은 주요한 정보 아래쪽에 부가적인 정보를 붙여주는 방식으로 나타난다.
  • 부가적인 정보를 왼쪽, 오른쪽에 붙여주는 방식이 존재하며 단순한 형태로 부가적인 정보를 보여주지 않는 방식도 존재한다.
  • 소개된 4가지 스타일은 테이블 뷰 내부에 이미 구현되어있지만 사용자 지정 방식을 통해 표현해주는 방법도 존재한다.

  • 사용자 지정 스타일을 사용하면 행의 모양은 원하는 모든 디자인이 될 수 있다.
  • 사용자 지정 스타일은 스토리보드에서 오토 레이아웃을 통해 만들어서 원하는 모든 종류의 UI를 빌드할 수 있다.
  • 컬렉션 뷰에서는 모든 셀이 전부 커스텀하다고 한다.

  • 위에서는 테이블 뷰의 섹션 스타일을 적용한 예시에 대해 확인해볼 수 있었다.
  • 테이블 뷰는 그룹화 될 수 있다. 대표적으로 iOS 의 설정앱이 존재할 것이다!
  • 테이블 데이터가 고정되어있는 경우만 그룹 스타일을 사용한다.
  • 시간이 지남에 따라 변할 수 있는 음식 정보만 보여주는 것이 아니라 설정앱처럼 내용이 변하지 않는 경우에 사용한다는 의미이다.

UICollectionView

  • Text 처럼 흐르는 방식으로 동작한다.
  • 테이블 뷰와 유사해 보이지만 컬렉션 뷰에는 row 가 존재하지 않는다.
  • 컬렉션 뷰에도 섹션을 설정해줄 수 있다.
  • 테이블 뷰 처럼 정보를 구분해줄 수 있는 개념이 존재하는 것이다.


TableView 와 CollectionView 를 불러오는 방법

  • object 팔레트에서 TableView 나 CollectionView 오브젝트를 불러온다.

  • MVC 의 전체 View 가 테이블 뷰로 사용되는 경우 TableViewController, CollectionViewController 를 선택하는 방법도 존재한다.
  • 테이블 뷰 컨트롤러를 사용하면 필요한 초기 사항이 설정되어있는 TableViewController 를 활용해보자!

어디서 어떻게 데이터를 불러오는가?

  • 테이블 뷰나 컬렉션 뷰가 어디서 데이터가 전달되며 어떻게 데이터를 가져오는가?
  • 이전 MVC 설명에서 View 는 데이터를 소유할 수 없다고 설명했다.
  • 테이블 뷰는 데이터를 소유할 변수를 갖고 있지 않으며 데이터를 "요청해야한다"
  • 데이터를 요청할 때 delegation과 동일한 방식으로 요청한다!
  • 테이블 뷰와 컬렉션 뷰는 datasource 라는 변수를 갖고 있으며 타입은 프로토콜이다.
  • 프로토콜에 정의된 행위는 데이터를 달라고 요청하는 메시지 들이다 (Ask!)
  • 계속 데이터를 달라고 요청하며 이 방식으로 테이블 뷰는 데이터를 받아서 보여준다.
  • 데이터 소스 프로토콜의 중요한 메서드에 대해 배워본 후에 테이블이나 컬렉션 뷰에 데이터를 불러오는 방법에 관하여 배운다.
  • 테이블 뷰와 컬렉션 뷰에는 delegate 라는 변수로 delegation 이 적용된다.
  • 위 변수는 어떻게 데이터가 표현되는지를 결정해준다.

  • 위에서 테이블 뷰를 사용하는 방법으로 View만 불러와서 수작업으로 환경설정 해주는 방식과
  • MVC 로 먼저 포장된 TableViewController 를 사용하는 예시를 확인했다.
  • TableViewController 를 사용하면 위 변수가 자동으로 설정되어있지만
  • View만 사용하는 경우 수동으로 위 변수를 설정해줘야한다.
  • 위 변수가 설정되면 View가 Controller와 소통하여 표현 방식을 결정하거나 데이터를 받아오는 과정이 가능해진다.

프로토콜에 정의된 핵심 메서드를 살펴보자!

  • 데이터를 받아오는 메서드가 12~15개 이상으로 많이 정의되어있다.
  • 그중에서도 다음과 같은 세가지 메서드는 가장 중요한 만큼 꼭꼭 기억해야한다!
  • IndexPath 는 섹션, 행, 아이템에관한 정보를 저장하고있는 작은 자료구조이다.
// 얼마나 많은 섹션이 존재하는가? -> 섹션이 구분되어있지 않다면 보통 1개가 존재한다!
func numberOfSections(in tableView:UITV) -> Int

// 얼마나 많은 행이 존재하는가?
func tableView(_ tv: UITV, numberOfRowsInSection section: Int) -> Int

// 각 행이나 item 에 관련한 정보를 가져오는 방법 IndexPath!!

// 특정 행이나 item에 관한 섹션 정보를 불러오고싶다면?
indexPath.section
// 테이블 뷰에서 특정 행에 관한 정보를 불러오고싶다면?
indexPath.row
// 컬렉션 뷰에서 특정 아이템에 관한 정보를 불러오고싶다면?
indexPath.item

// 테이블 뷰에서의 각 행이나 컬렉션 뷰에서의 아이템을 불러오는 방법
func tableView(_ tv:UITV, cellForRowAt indexPath: IndexPath) -> UITableViewCell

Cell

  • 각 행에 대한 데이터를 가져오는 것이 중요하며 테이블 뷰나 컬렉션 뷰에 보여줘야할 데이터가 복잡할 수 있기 때문이다.
  • indexPath 를 통해 특정 행이나 섹션에 들어가야 할 데이터를 지정해줄 수 있다.
  • indexPath 에 있는 Cell 에 관하여 좀더 자세하게 알아볼 것이다.
  • tableView(_ tv: UITV, cellForRowAt indexPath: IndexPath) 메서드는 UIView 의 일종인 UITableViewCell 을 반환한다.
  • 테이블 뷰의 행을 그리는데 사용되는 View이며 하는 역할은 단순해보인다.
  • 메서드 구현부의 첫번째 줄에 있는 dequeueReusableCell(withIdentifier:for) 과 같은 기괴한 메서드를 호출하는 모습이 보인다.
  • 무슨 일이 일어나고 있는 것일까?

Cell Reuse

  • 재사용 메커니즘을 사용한 이유는 무엇일까?
  • 예를 들어 10,000 곡의 노래가 담겨있는 iPod 라이브러리가 존재한다고 가정해보자!
  • 각 View에는 노래 데이터 뿐만아니라 앨범 아트에 관한 정보가 추가적으로 붙어있을 것이다.
  • 이  모든 데이터에 관하여 View 를 갖는 것은 매우 비효율적인 방식으로 생각할 수 있다.
  • table view는 실제로 사용자의 눈에 보이는 행에 대해서만 테이블 cell 을 생성한다.
  • 사용자가 스크롤을 내리면 상단에서 데이터가 날아가서 재사용하며 다시 하단으로 이동시킨다.
  • 따라서 스크롤 과정에서 셀을 지속적으로 재사용하게된다.
  • 사용자가 스크롤을 바꾸면서 보이지 않는 Cell 이 발생하게될것이며
  • UIVIew 인 UITableView Cell 은 재사용 풀로 이동하게 될 것이다.
  • 해당 메서드인 dequeReusable 을 통해 재사용하려는 셀을 얻어서 다시 자료를 담아 재사용할 수 있다!

Cell Creation

  • 만약 reusable pool 이 비어있다면 어떤 문제가 발생하게 될 것인가?
  • 정답은 -> 스토리 보드에서 정의된 프로토타입을 복사하여 새로운 cell 을 생성해준다는 점이다.
  • 따라서 오토레이아웃과 UIKit 요소를 갖고 Cell 프로토타입을 스토리보드에 구현해두면 내부적으로 필요한 상황이 올 때마다 복사해서 사용한다는 의미이다.
  • 다음은 두개의 재사용 가능한 셀의 프로토타입을 정의한 예시이다.
  • Style 과 Identifier 를 지정해주고 있는데 Xcode의 인스펙터 창에서 지정해줄 수 있다.
  • identifeir은 재사용 가능 셀들중 현재 필요한 셀을 구분하기위해 사용된다.
  • 재사용 풀에서 원하는 셀을 찾아보다가 찾고자 하는 셀이 존재하지 않으면 storyboard의 프로토타입에서 새로운 셀을 복사해온다.


  • Cell 을 재사용하는 과정은 멀티스레딩 과정에서 문제를 발생시킬 수 있다.
  • 예를 들어 비동기적으로 네트워크에서 이미지를 불러오는 동안 사용자가 스크롤을 이동해서 Cell이 재사용되었다고 가정해보자.
  • 네트워크에서 불러오는 작업이 완료된 후에 다시 Cell 을 갱신했는데 이미 해당 Cell 이 다른 용도로 재사용 된 경우라면 문제가 발생할 것이다.
  • 즉, 셀의 이전 상태가 새로운 상태에 영향을 주게된 것이다.
  • 이런 문제가 발생하지 않도록 구현하려면 현재 Cell 이 보여주려고 요청한 데이터를 여전히 보여주고 있는지 확인해보아야한다.
  • 본래 스레드로 돌아왔을 때 여전히 같은 상황인지 확인해보아야 한다는 점이다.

  • decision 을  통해 데이터나 상황에 맞추어 원하는 셀을 선택하는 코드를 작성하기도 한다.
  • 예를들어 어떤 음식은 기본 적인 설명만 있을 수 있고 다른 음식은 세부 설명을 포함하고 있을 수 있지 않는가?
  • 이렇게 정보에 알맞은 셀을 선택하기 위해 decision 을 사용하기도 한다! 

  • 이제 Cell 도 만들었으니 Cell에 어떻게 데이터를 반영시켰는지에 관하여  이해해보자!
  • 만약 basic 스타일의 셀을 사용할 경우 자동으로 설정된 textLable, detailTestLabel 아울렛을 사용하면 된다.

  • 사용자 정의 셀을 사용할 경우 CustomFoodCell 이라는 사용자 정의 custom 클래스를 정의해줘야한다.
  • 가장 단순하게 생각해보면 UITableViewController 를 상속받은 클래스에 아울렛을 설정해줘야 할 것 같다.
  • 여러개의 테이블 뷰 셀이 존재할 수 있는데 TableView 에 단 하나의 아울렛으로 표현하기는 어렵다.
  • 또한 오랜 시간동안 셀이 재사용되기 때문에 컬렉션으로 가져오는 것도 좋은 방식이 아닐 수 있다.

  • 실제로 아울렛을 뷰에 배치 시킬 것이며 CellForRowAt 이 반환하는 UITableViewCell 타입이어야 한다.
  • 놀랍게도 outlet 을 View 에 위치시키고 있다.
  • 테이블 뷰에는 행이 존재하고 행에는 셀이 존재하며 셀에 outlet이 담긴 구조가 만들어졌다.
  • 사용자 정의 클래스를 만들어준 경우 identity 인스펙터에서 프로토타입 셀의 클래스를 바꿔줘야한다.

  • 문제가 있다! 현재 Cell 은 사용자 정의 타입으로 만들어진 cell 인지, 기본 스타일의 cell 인지 구분하지 못하기 때문이다.
  • 즉, 현재 상태에서 꺼내온 cell 이 outlet이 선언된 사용자 정의 Cell 일지라도 다운캐스팅을 통해 세부적으로 구분해주지 않으면 사용하지 못한다.


정적인 데이터를 보여주는 테이블 뷰

때때로 테이블 뷰는 UI 레이아웃에 사용된다. 매번 데이터를 받아오는 것이 아닌 애초부터 고정된 양의 데이터를 보여주는 것이다. 일반적으로 설정앱 과 같은 것이 고정된 데이터를 보여주고 있으며 행을 그룹핑해서 보여주고 있다. 

이런 상황에서는 위에서 작업한 복잡한 짓거리를 할 필요가 없다. 모두 스토리보드에서 진행하며 그냥 컨트롤러와 직접 연결해서 사용하기만 하면 된다. 왜냐하면 정적이기 때문에 프로토타입으로부터 복사될 필요가 없기 때문이다. 실제로 자주 사용된다. 

변경하는 방법은 스토리보드에서 테이블 뷰를 클릭한 후에 content 를 static cell 로 변경해주면 되고, Grouped 스타일이 사용되는 경우가 많다!

고정 테이블 뷰에 그룹 스타일을 적용하면 인스펙터를 통해 섹션의 개수를 직접 지정해줄 수 있다.

이제 사용자가 드래그하던 버튼을 클릭하던 컨트롤러가 알아서 다 제어해준다. 왜냐하면 정적 테이블 뷰이기 때문이다.


Storyboard!

  • 셀의 액세서리 부분을 설정해줄 수 있다!
  • 다음의 이미지는 Cell 의 액세서리로 Detail Disclosure Accessory 를 설정한 예시이다.
  • 우측에 원형모양의 i 아이콘이 적용된 것을 확인할 수 있다.
  • 이 상황에서 해당 행이 선택되었을 때 화면 전환을 발생시킬 수 있다.
  • 화면전환을 할 때 테이블 뷰의 행으로부터 segue를 연결해줄 수 있지만 액세서리를 통해 별도의 segue를 지정해주는 것도 가능하다.

  • 프로토타입에서 segue 를 이어주는 방식도 control + 드래그 메커니즘으로 이뤄진다.
  • selection segue 를 설정해주면 테이블뷰 셀이 클릭되었을 때 화면 전환이 이뤄지는데
  • Accessory Action 을 설정해주면 원형모양의 (i) 액세서리 뷰가 클릭되었을 때 화면 전환이 이뤄진다.

  • segue 에도 identifier를 지정해줄 수 있다.
  • 정적으로 진행되는 경우에는 위와 같은 방식으로 스토리보드 상에서 segue 를 연결해줄 수 있지만
  • 동적으로 생성되는 테이블 뷰 셀에서 화면 전환이 발생하는 경우 prepare 메서드를 통해 segue 요청이 발생한 행을 알아내야 한다.

  • segue를 prepare 하는 방법이다.
  • prepare 메서드의 핵심적인 인자는 sender 객체이다.
  • target-action 메커니즘에서 버튼을 클릭할 때마다 sender에 해당 버튼 객체가 담겨오는 것처럼
  • prepare 메서드에는 클릭된 cell 이 담겨온다.
  • 클릭된 cell 과 연관된 데이터를 알기 위해서는 테이블 뷰 상에서 sender에 담긴 cell의 IndexPath를 알고있어야 하며
  • IndexPath 를 알고 있으면 모델로 부터 해당 셀에 담겨야할 데이터를 불러올 수 있게된다.
  • segue로 부터 destination ViewController 정보를 불러올 수 있기 때문에 내가 이동되길 기대하는 ViewController 로 다운캐스팅해서 자료를 넘겨줄 수 있다.
  • 하지만 현재 sender 를 any 타입으로 받고 있기 때문에 sender 를 UITableViewCell 로 다운캐스팅 해주거나 인자의 타입을 바꿔줘야 IndexPath에 접근할 수 있게된다.


Model이 변경되는 경우

  • 예를 들어 iPod 에 담겨있는 음악을 추가한다거나 새로운 음식을 추가하는 경우가 있을 수 있다.
  • 테이블 뷰와 컬렉션 뷰에는 reloaddata() 메서드가 존재한다.
  • 이 메서드는 행의 개수 섹션의 개수, 테이블뷰cell 에 관한 모든 정보를 갱신해주는 작업을 해준다.
  • IndexPath 에 대해서 다시말해 좀더 세분화된 cell 에 대해서만 데이터를 갱신해주는 방법도 있다.
  • 모든 데이터를 바꾼다는게 조금 이상할 수 있디만 모델이 변경되면 테이블도 변경되어야 한다는 점을 생각해보아야 한다.
  • 테이블과 모델이 오랬동안 동기화되지 않는 문제점이 발생될 수 있기 때문이다.
  • 다음은 테이블 뷰의 행 크기에 대해 조정시켜주는 방법이다.

  • 다음은 테이블 뷰의 header를 지정해주는 방법이다.

반응형