Table views in iOS display rows of vertically scrolling content in a single column. Each row in the table contains one piece of your app’s content.
그림과 함께 확인해보면, 수직 방향으로 스크롤 하면서 정보를 보여주는 형태입니다.
UITableView manages the basic appearance of the table, but your app provides the cells (UITableViewCell objects) that display the actual content.
공식문서를 더 살펴보면 UITableView
를 통해서 table의 기본적인 외형을 관리 하지만 실제 내용을 보여주는 cell
을 제공한다고 합니다.
처음보는 용어들이 많으니, 시작하기 앞서서 전체적인 용어를 정리하고 시작하겠습니다.
Table View
는 크게 Header, Section1 , Section2 ..., Footer
형태로 구성됩니다.
또, 각 Section
은 Header, Cell1, Cell2, ..., Footer
형태로 구성되어 있습니다.
앞선 공식문서에 확인한 Table View
의 역할은 이런 전체적인 구성을 관리하는 것입니다.
그리고 Table View Cell
을 통해서 우리가 Table을 통해서 구체적으로 데이터를 어떻게 표현할지를 설정할 수 있습니다.
1 ) Content
Table View
는 어떤 데이터를 사용자에게 보여주는지에 따라 두 가지로 나뉩니다.
- Static Cells
- Dynamic Prototypes
Static 은 변하지 않는 모든 사용자가 일관된 데이터를 보여줄 때 사용됩니다.
ex) 카카오톡 설정 화면
Dynamic 은 사용자에 따라 변하는 데이터를 보여줄 때 사용됩니다.
ex) 카카오톡 채팅방 목록 화면
2 ) Style
- plain
- grouped
- insetGrouped
(iOS 15 default)
Style 에 따라 위의 모양처럼 바뀌는 것을 확인 할 수 있습니다.
Table View
예제를 살펴보기 전에 cell 을 설정하는 Table View Cell
도 간단하게 짚고 넘어가겠습니다. (공식문서)
1 ) style
- Basic, Subtitile, Right Detail, Left Detail
- Custom
(첫번째 스타일들은 애플이 미리 만들어준 디자인입니다.)
해당 블로그 에서 각 스타일별 이미지들을 확인 하실 수 있습니다.
2 ) Accessory View & Edit Mode
Accessroy View
를 이용하면 그림과 같이 cell 의 우측에 indicator 나 checkmark 와 같은 Accessory 를 배치할 수 있습니다.
edit mode
에서는 cell 의 좌측에 Delete control
를 이용 할 수 있습니다. ex) iOS 알람에서 시간 삭제
간단한 예제를 만들어서 더 자세히 알아보겠습니다.
우선, Table View
를 만드는 방법은 크게 2 가지가 있습니다.
View Controller
에 Table View
와 Table View Cell
을 추가Table View Controller
를 이용❗️Table View Controller
를 이용할 경우 Root View 가 Table View 가 되기 때문에 다른 View 객체들을 넣을 수 없습니다.
오늘은 두번째 방법을 이용해 간단한 예제를 살펴 보겠습니다.
우선 Library 를 통해서 Table View Controller
를 추가합니다.
그리고 Table View Controller
를 상속받는 로직 파일을 생성하고 씬과 클래스를 연결합니다. (❗️주의)
그리고 코드를 살펴보도록 하겠습니다.
1 ) 셀의 개수 : numberOfRowsInSection
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return setOverall.count
//return 100
}
else if section == 1 {
return setPersonal.count
}
else {
return etc.count
}
}
Table View
가 몇 개의 cell 로 구성되어 있는지 iOS 시스템에 알려줘야 합니다. 위의 코드처럼 각 section 별로 cell 의 개수를 다르게 지정할 수도 있습니다.
2 ) 셀의 디자인 및 데이터 처리 : cellForRowAt
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(#function)
//Early Exit
guard let cell = tableView.dequeueReusableCell(withIdentifier: "setCell") else {
return UITableViewCell()
}// nil 값을 가지면 return 되서 종료된다. (cell의 옵셔널이 지워짐)
// ? 는 뭘까? -> 옵셔널 체이닝
if indexPath.section == 0 {
cell.textLabel?.text = setOverall[indexPath.row]
cell.textLabel?.textColor = .brown
cell.textLabel?.font = .boldSystemFont(ofSize: 15)
}
else if indexPath.section == 1 {
cell.textLabel?.text = setPersonal[indexPath.row]
cell.textLabel?.textColor = .blue
cell.textLabel?.font = .italicSystemFont(ofSize: 13)
}
else {
cell.textLabel?.text = etc[indexPath.row]
cell.textLabel?.textColor = .green
cell.textLabel?.font = .italicSystemFont(ofSize: 13)
}
return cell
}
각 셀의 디자인과 데이터를 어떻게 처리할지를 설정해줘야 합니다. 위의 코드처럼, 각 섹션별로 다른 디자인을 적용할 수 있습니다.
❗️table view cell 의 identifier 설정해야 합니다!
dequeueResuableCell 과 ? 는 마지막에 다루도록 하겠습니다.
3 ) 셀의 높이 : heightForRowAt (default = 44)
//섹션마다, 셀마다 높이를 다르게 설정 가능
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
각 셀의 높이를 설정할 수 있습니다. (default = 44)
각 섹션, 행 별로 높이를 다르게 설정할 수 있습니다.
4 ) 셀을 클릭했을 때 기능 : didSelectRowAT
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("셀 선택")
}
ex) 배달의 민족 앱에서 메뉴 추가 선택 시, 버튼 처럼 보이는 부분뿐만 아니라, 그 외의 오른쪽 영역을 선택해도 추가되는 형태
5 ) 스와이프 했을 때 삭제 : editingStyle
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if editingStyle == .delete {
setOverall.remove(at: indexPath.row)
tableView.reloadData()
}
}
else if indexPath.section == 1 {
if editingStyle == .delete {
setPersonal.remove(at: indexPath.row)
tableView.reloadData()
}
}
else if indexPath.section == 2 {
if editingStyle == .delete {
etc.remove(at: indexPath.row)
tableView.reloadData()
}
}
}
해당 코드를 통해서 cell 을 오른쪽에서 왼쪽으로 스와이프 했을 때, 해당 cell 을 삭제 할 수 있습니다.
❗️해당 cell의 데이터를 삭제하고 이를 table view 가 갱신하도록 reloadData() 를 통해서 알려줘야 한다.
6 ) 섹션의 수 : numberOfSections(default = 1)
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
위의 코드에서 indexPath.section , indexPath.row
라는 값들을 많이 사용했는데 무엇을 의미하는 걸까요?
그림과 같이 특정 섹션과 특정 행에 대한 위치를 인덱스를 통해서 표현한 것입니다.
이를 이용하면, 우리가 원하는 cell 에 쉽게 접근할 수 있습니다!
우선 Optional Binding
에 대해서 먼저 알아 보겠습니다.
Optional Binding
은 옵셔널 변수를 열어봤는데 만약 값이 있으면 변수에 할당한다. 정도로 생각하면 될것 같습니다.
1 ) if
if let cell = tableView.dequeueReusableCell(withIdentifier: "setCell") {
print("")
}
else {
print("옵셔널")
}
옵셔널 값 tableView.dequeueReusableCell(withIdentifier: "setCell")
이 nil 일 경우에는 cell 이라는 상수에 nil 이 저장되고 else 구문이 실행됩니다.
nil이 아닌 경우에는 cell 에 해당값이 저장되고 if 문이 실행됩니다.
2 ) guard
guard let cell = tableView.dequeueReusableCell(withIdentifier: "setCell") else {
return UITableViewCell()
guard 는 if 를 사용한 Binding 과 달리, else 반드시 명시해줘야 합니다.
cell 의 값이 nil 이 되는 경우, else 구문이 실행되 함수가 종료됩니다.
if 는 조건에 부합하지 않는 경우 else if, else if, else 등 모든 case 를 확인하지만 guard 는 바로 else로 가서 함수를 종료하기 때문에 early exit 이라고 하지 않을까?
옵셔널 체이닝
중첩된 옵셔널 중 하나라도 값이 존재하지 않으면 nil 을 반환하는 형식을 의미합니다.
옵셔널 바인딩이 있는데 왜 옵셔널 체이닝을 사용할까요❓
import UIKit
class Room {
var number: Int
init(number: Int) {
self.number = number
}
}
class Building {
var name: String
var room: Room?
init(name: String) {
self.name = name
}
}
struct Address {
var province: String
var city: String
var street: String
var building: Building?
var detailAdress: String?
}
class Person {
var name: String?
var adress: Address?
init(name: String) {
self.name = name
}
}
let sangwon: Person = Person(name: "sangwon")
let sangwonRoomViaOptionalChaining: Int? = sangwon.adress?.building?.room?.number
sangwon.adress?.building?.room?.number = 202
위와 같은 코드가 있을때, 옵셔널 바인딩을 사용하게 되면 코드가 복잡해 지는 것을 알 수 있습니다. (if { if { if ~)
이때, 옵셔널 체이닝을 사용하면 훨씬 간단하게 표현할 수 있습니다.
❗️ 옵셔널 체이닝은 옵셔널 값이므로 바인딩과 함께 사용할 수 있습니다.
❗️ 옵셔널 체이닝을 통해 값을 할당해줄 수도 있습니다.
를 참고해서 정리하고 있습니다.
교육을 들으면서 제일 정리할게 많았던 하루였습니다 (ㅠㅠ)
cell의 재사용 매커니즘
이 왜 필요한지는 이해 했으나 아직까지 어떻게 구체적으로 동작하는지를 이해하지 못했습니다. 조금 더 고민하고 생각이 정리되면 다시 정리하도록 하겠습니다!