본문 바로가기
공부/[iOS&Swift]

[iOS] UITableView에 대하여

by 인생은아름다워 2022. 2. 21.

 

상용 앱을 쓰다 보면 오늘의 주제인 TableView가 상당히 많이 사용되고 있음을 알 수 있다. 이 테이블 뷰의 예시는 너무 많아서 굳이 언급하지 않아도 될 것 같다. 그만큼 테이블 뷰에 대한 정확한 정의와 언제 어떻게 사용하는지에 대해서 잘 정리해둬야 할 것 같다. 오늘은 그래서 TableView에 대한 내용 정리와 간단하게 어떻게 사용하는지 예시를 들어볼까 한다.

✏️ UITableView를 언제 사용하는것인가?

테이블 뷰를 써서 얻을 수 있는 이점이 뭔지 생각해봤다. 아무래도 테이블 뷰는 반복되는 UI가 가변하는 데이터들을 나타낼 때 사용하면 좋을 것 같다 라는 정리를 했다.

  1. 데이터 가변성
  2. 유사한 UI의 반복

위와 같은 상황에 테이블뷰를 적절히 사용하는 것은 앱을 구성하는데 큰 도움이 될 것이라고 생각한다.

다만, 이 또한 트레이드오프가 존재할 텐데 이 글을 통해 trade-off 관계를 잘 숙지하여 어떤 경우에 테이블 뷰를 사용할지에 대해 참고해보면 좋을 것 같다.

👉 **UITableView를 쓰지 말아야 할 때 (정확히는 고민해보고 쓰자는 글!)**


✏️ UITableView에 대하여

UITableView는 위와 같이 하나의 열에 여러 개의 행들을 이용하여 데이터를 나타내는 뷰이다. 그리고 테이블 뷰는 UIScrollView를 상속받고 있음을 알 수 있다.

테이블 뷰 : 이렇게 한 행마다 각 데이터를 가지고 있는 스크롤이 되는 뷰!

실제로 많은 앱에서 계층적으로 데이터를 포함하는 경우 네비게이션 컨트롤러 + 테이블 뷰 를 통해 다른 계층 간의 데이터 탐색을 구현하고 있다. 위 사진에서 예로 든 Settings앱이 좋은 예시이다.

간단히 이 정도로 테이블 뷰가 어떤 건지 그리고 어떻게 사용하는 건지 알면 될 것 같고, 가장 중요한 사용법에 대해서 알아봐야 할 것 같다.

✏️ TableView 사용하기

TableView 또한 ViewController에 포함되어 동작하게 될 것이다. 그런데 문제는 이 테이블에서 행의 개수는 분명 가변 될 수 있고, UI의 모양이라든지 그 내부의 속성들도 당연히 가변될 수 있는 것이다.

 

그렇기 때문에 무작정 TableView의 인스턴스를 생성해서 다루는 것이 아니라 ViewController와 TableView 간에는 protocol을 통해 delegate pattern으로 소통하게 된다!

→ delegate pattern에 대한 이해는 iOS 앱을 개발함에 있어서 정말 중요한 것 같다.(그런데 아직 난 잘 이해하지 못했다🤣)

 

방금 말한 것과 같이 테이블 뷰를 이용하려면 테이블뷰와 뷰컨트롤러간의 두 프로토콜을 이용해야 하며(이 부분은 그냥 받아들여야되나봅니다..) 두 프로토콜은 UITableViewDataSource와 UITableViewDelegate이다.

 

그리고 테이블뷰 안의 콘텐츠가 될 cell들에 대해서도 고민을 해봐야 한다. 이 셀을 만드는데 여러 가지 방법이 있지만, 이번 글에서는 커스텀 셀을 만드는 법에 대해서만 설명하고자 한다.


다음과 같은 순서로 테이블 뷰를 만들어보자

  1. 테이블 뷰 생성
  2. 테이블 뷰의 delegate를 구성
  3. 테이블 뷰 delegate 채택 및 구현(?)
  4. Cell을 테이블 뷰에 등록
  5. 셀 생성
  6. 데이터 전달

1. 테이블 뷰 생성하기

먼저 이렇게 인터페이스 빌더에서 최상단 view안에 UITableView하나를 원하는 위치에 레이아웃을 잡아준다.

그래고 인터페이스 빌더에 IBOutlet을 통해 연결해주면 1번 과정이 끝난다. 아직까진 매우 간단하다.

2. 테이블 뷰의 delegate 구성하기

테이블 뷰를 포함하는 뷰 컨트롤러가 바로 테이블 뷰의 delegate가 되어야 한다.

뷰 컨트롤러 내부에 아래와 같은 코드를 작성해준다.

@IBOutlet weak var tableView: UITableView! // 1번의 내용

override func viewDidLoad() {
	// ...
	setupTableView()
}

private func setupTableView() {
	self.tableView.delegate = self
	self.tableView.dataSource = self
}

여기까지도 할 만한 것 같다.

3. 테이블 뷰 delegate 채택 및 구현 (셀 및 모델 구현 포함)

뷰 컨트롤러 내부에서 진행해도 되지만, 코드의 분리를 위해 extension을 이용해보자.

extension ViewController: UITableVIewDelegate, UITableViewDataSource {
	
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
	// code 작성
}

	func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
		// code 작성	
	} 
}

위와 같이 두 프로토콜을 채택하는 것만으로도 코드 블록 안의 두 method를 구현해야 한다고 친절하게(?) 에러를 통해서 알려줄 것이다.

위 메서드들을 살펴보면 이름을 통해서 유추해볼 수 있는데 하나는 몇 행을 만들 것인지에 대한 메서드, 다른 하나는 어떤 셀을 사용할 것인지에 대한 메서드이다.

 

테이블 뷰는 유사한 UI와 가변 하는 데이터라고 했다. 그 중에서 유사한 UI라는 부분을 이 메서드들을 통해 작성할 수 있는 것이다. → 가변하는 데이터에 대해서는 나중에 나올 예정!

 

이쯤에서 메서드를 구현하기에 앞서서 어떤 데이터들을 채워나갈지에 대해서 타입과 모델을 만들어보자.

//Item.swift

struct Item {
    let name: String
    let image: String
}

//ItemModel.swift

class ItemModel {
    
    private var itemOnSale: [Item] = []
    
    init() {
        fetchData()
    }
    
    public var numOfItems: Int {
        return itemOnSale.count
    }
    
    public func itemAt(_ row: Int) -> Item {
        return self.itemOnSale[row]
    }
    
    public func fetchData() {
        // 실제로는 서버에서 받아와서 itemOnSale에 넣는 작업을 수행하는 method
        
        itemOnSale = [
            Item(name: "QuestionMark", image: "questionmark"),
            Item(name: "ExclamationMark", image: "exclamationmark"),
            Item(name: "Person", image: "person"),
            Item(name: "Car", image: "car")
        ]
    }
    
}

이렇게 두 swift파일을 생성하여 테이블 뷰에 넣을 데이터 모델에 관한 작업을 해 두었다.

하나는 Item이라는 타입을 구조체로 정의하였으며, 다른 하나는 실제 Item 타입을 이용하여 테이블 뷰를 구성할 모델을 만든 것이다.

그리고 아래 두 과정을 통해서 테이블 뷰에 들어갈 셀을 xib파일로 만들어보자.

그리고 만들어진 xib파일과 UITableViewCell 타입의 클래스 내에 평소 스토리보드 내에서 잡아주던 UI를 구성한다. 그리고 가변 하는 데이터를 처리하기 위해서 아래 사진과 같이 TableViewCell클래스 내에 setupData(data:) 메서드를 생성했다.

4. Cell을 TableView에 등록하기

private func setupTableView() {
	// ...
	self.tableView.register(
		UINib(nibName: "TableViewCell", bundle: nil),
		forCellReuseIdentifier: TableViewCell.identifier)     //추가
}

이 코드를 통해, TableView에 위에서 만든 xib파일을 셀로 사용하겠다고 등록하는 과정을 진행한다.

만약 여러 개의 xib파일을 가지고 한 테이블 뷰 안에서 상황에 따라서 쓰고싶다면, 이 곳에서 여러개의 xib파일을 등록해주면 되지 않을까...?

5. & 6. 셀을 생성하고, 셀에 데이터를 전달하기

모든 과정을 마무리하는데, 아래의 코드를 얼마나 이해했는지가 의미하는 것이 결국 tableView를 얼마나 잘 커스텀해서 쓸 수 있는지?라는 질문에 대한 답과 같을 것이다.

// ViewController.swift

class ViewController: UIViewController {

// ...
let tableViewItems = ItemModel() // 테이블 뷰에 넣을 실제 데이터 모델
// ...
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {

	func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
		return tableViewItems.numOfItems // 데이터 모델 내 데이터의 개수
	}

	func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
		
		guard let cell = tableView.dequeueReusableCell(
			withIdentifier: TableViewCell.identifier,
			for: indexPath) as? TableViewCell else { return UITableViewCell() }

		let cellData = self.tableViewItems.ItemAt(indexPath.row) // 어떤 데이터를 넣고싶은지 ?
		cell.setupData(cellData) // 그 데이터를 실제로 셀에 넣어준다.

		return cell // 이 셀을 그린다.

	}
}

위의 1~6 과정을 거쳐서 나온 테이블 뷰는 아래와 같다.

뭐 형편없어 보이지만, 결국 여기서부터 모든 것이 시작되는 것 아닐까...?

TableView와 NavigationView를 잘 엮어서 클론 코딩을 하나 해 볼까 한다!

 

🍎 결론

테이블 뷰 정말 많이 쓰게 될 것 같다.

아직 셀을 커스텀해서 등록하고 사용하는 과정이 익숙하지 않다.

많이 해 봐야겠다.

댓글