[Day 42] iOS UITableView 셀이 보이지 않는 문제 해결 (ViewController와 View 인스턴스 참조 오류)
iOS 앱 개발 중 UITableView 셀이 보이지 않는 문제를 디버깅하고, ViewController와 View 인스턴스 참조 오류를 해결한 과정에 대한 TIL.
[Day 42] iOS UITableView 셀이 보이지 않는 문제 해결 (ViewController와 View 인스턴스 참조 오류)
오늘은 CurrencyCalculatorApp 프로젝트에서 UITableView 셀이 화면에 전혀 보이지 않는 문제로 인해 많은 시간을 보냈다. 테이블 뷰 자체는 화면에 잘 나타났지만, 그 안에 데이터가 채워진 셀은 아무리 디버깅해도 나타나지 않았다.
문제 현상
UITableView는 화면에 정상적으로 표시됨.loadData()함수를 통해 데이터(items배열)는 163개로 정상적으로 로드됨.tableView.reloadData()호출도 확인됨.UITableViewDataSource의numberOfRowsInSection은items.count(163)를 반환함.- 하지만
tableView(_:cellForRowAt:)메서드 내부의print구문이 전혀 호출되지 않음. - 셀의 배경색이나 텍스트 색상을 변경해도 아무것도 보이지 않음.
디버깅 과정
- 데이터 로드 확인:
items배열에 데이터가 정상적으로 들어오는지print구문을 통해 확인했다. 결과는 163개의 데이터가 잘 로드되고 있었다. 이는 데이터 문제로 셀이 안 보이는 것이 아님을 의미했다. cellForRowAt호출 여부 확인:tableView(_:cellForRowAt:)메서드 내부에print구문을 추가하여 이 메서드가 호출되는지 확인했다. 놀랍게도 아무것도 출력되지 않았다. 이는 테이블 뷰가 셀을 요청하지 않고 있다는 뜻이었다.- 델리게이트/데이터소스 설정 확인:
viewDidLoad()에서self.currencyView.currencyTableView.delegate = self와self.currencyView.currencyTableView.dataSource = self가 설정되어 있음을 확인했다. 문법적으로는 문제가 없어 보였다. ViewController와CurrencyView인스턴스 불일치 의심:tableView(_:cellForRowAt:)가 호출되지 않는다는 것은dataSource가 제대로 연결되지 않았다는 강력한 증거였다.ViewController의 초기화 부분을 다시 살펴보았다.
문제의 원인
문제는 ViewController에서 CurrencyView 인스턴스를 다루는 방식에 있었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ViewController: UIViewController {
private var currencyView = CurrencyView() // (1) 여기서 CurrencyView 인스턴스 생성
override func loadView() {
view = CurrencyView() // (2) 여기서 또 다른 CurrencyView 인스턴스 생성하여 view에 할당
}
override func viewDidLoad() {
super.viewDidLoad()
// (3) currencyView 속성의 테이블 뷰에 델리게이트/데이터소스 설정
self.currencyView.currencyTableView.delegate = self
self.currencyView.currencyTableView.dataSource = self
loadData()
}
}
private var currencyView = CurrencyView():ViewController의 속성으로CurrencyView인스턴스가 하나 생성된다.override func loadView() { view = CurrencyView() }:loadView()는 뷰 컨트롤러의view속성을 로드할 때 호출된다. 여기서 새로운CurrencyView인스턴스를 생성하여view에 할당한다.viewDidLoad()에서self.currencyView.currencyTableView.delegate = self:viewDidLoad()에서는 (1)에서 생성된currencyView속성의 테이블 뷰에 델리게이트와 데이터 소스를 설정한다.
결과적으로, 화면에 실제로 보이는 view (2번 인스턴스)와 델리게이트/데이터 소스가 설정된 currencyView 속성 (1번 인스턴스)이 서로 다른 객체였던 것이다. 화면에 보이는 테이블 뷰는 델리게이트와 데이터 소스가 설정되지 않았으므로, cellForRowAt 메서드를 호출할 수 없었던 것이다.
해결책
문제는 ViewController의 currencyView 속성과 view 속성이 서로 다른 CurrencyView 인스턴스를 참조하고 있었기 때문이었다. 이를 해결하기 위해 loadView() 메서드에서 view = CurrencyView() 대신, 이미 초기화된 currencyView 속성 인스턴스를 view에 할당하도록 수정했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ViewController: UIViewController {
private let currencyService = CurrencyService()
private var currencyView = CurrencyView() // 기존과 동일하게 초기화
private var items: [CurrencyItem] = []
override func loadView() {
view = currencyView // 이미 초기화된 currencyView 인스턴스를 view에 할당
}
override func viewDidLoad() {
super.viewDidLoad()
// 이제 화면에 보이는 테이블 뷰에 델리게이트/데이터소스가 올바르게 설정됨
self.currencyView.currencyTableView.delegate = self
self.currencyView.currencyTableView.dataSource = self
loadData()
}
// ... 나머지 코드
}
이 수정으로 인해 ViewController의 currencyView 속성과 view 속성이 동일한 CurrencyView 인스턴스를 참조하게 되었고, viewDidLoad()에서 설정한 델리게이트와 데이터 소스가 화면에 보이는 테이블 뷰에 올바르게 적용되어 셀이 정상적으로 표시되었다.
배운 점
ViewController에서view를 커스텀 뷰로 설정할 때는loadView()메서드 내에서view에 할당하는 인스턴스와,ViewController의 다른 속성으로 참조하는 인스턴스가 동일한 객체인지 항상 주의해야 한다.- 테이블 뷰 셀이 보이지 않을 때는 데이터 로드 문제뿐만 아니라, 델리게이트/데이터소스 연결, 뷰 계층 구조, 그리고 뷰 컨트롤러의 뷰 초기화 방식까지 폭넓게 디버깅해야 한다.
혹시 이 글을 보시는 분들 중 더 유용한 팁이 있다면 댓글로 많이 알려주세요!