Post

[Day 42] iOS UITableView 셀이 보이지 않는 문제 해결 (ViewController와 View 인스턴스 참조 오류)

iOS 앱 개발 중 UITableView 셀이 보이지 않는 문제를 디버깅하고, ViewController와 View 인스턴스 참조 오류를 해결한 과정에 대한 TIL.

[Day 42] iOS UITableView 셀이 보이지 않는 문제 해결 (ViewController와 View 인스턴스 참조 오류)

[Day 42] iOS UITableView 셀이 보이지 않는 문제 해결 (ViewController와 View 인스턴스 참조 오류)

오늘은 CurrencyCalculatorApp 프로젝트에서 UITableView 셀이 화면에 전혀 보이지 않는 문제로 인해 많은 시간을 보냈다. 테이블 뷰 자체는 화면에 잘 나타났지만, 그 안에 데이터가 채워진 셀은 아무리 디버깅해도 나타나지 않았다.

문제 현상

  • UITableView는 화면에 정상적으로 표시됨.
  • loadData() 함수를 통해 데이터(items 배열)는 163개로 정상적으로 로드됨.
  • tableView.reloadData() 호출도 확인됨.
  • UITableViewDataSourcenumberOfRowsInSectionitems.count (163)를 반환함.
  • 하지만 tableView(_:cellForRowAt:) 메서드 내부의 print 구문이 전혀 호출되지 않음.
  • 셀의 배경색이나 텍스트 색상을 변경해도 아무것도 보이지 않음.

디버깅 과정

  1. 데이터 로드 확인: items 배열에 데이터가 정상적으로 들어오는지 print 구문을 통해 확인했다. 결과는 163개의 데이터가 잘 로드되고 있었다. 이는 데이터 문제로 셀이 안 보이는 것이 아님을 의미했다.
  2. cellForRowAt 호출 여부 확인: tableView(_:cellForRowAt:) 메서드 내부에 print 구문을 추가하여 이 메서드가 호출되는지 확인했다. 놀랍게도 아무것도 출력되지 않았다. 이는 테이블 뷰가 셀을 요청하지 않고 있다는 뜻이었다.
  3. 델리게이트/데이터소스 설정 확인: viewDidLoad()에서 self.currencyView.currencyTableView.delegate = selfself.currencyView.currencyTableView.dataSource = self가 설정되어 있음을 확인했다. 문법적으로는 문제가 없어 보였다.
  4. ViewControllerCurrencyView 인스턴스 불일치 의심: 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()
  }
}
  1. private var currencyView = CurrencyView(): ViewController의 속성으로 CurrencyView 인스턴스가 하나 생성된다.
  2. override func loadView() { view = CurrencyView() }: loadView()는 뷰 컨트롤러의 view 속성을 로드할 때 호출된다. 여기서 새로운 CurrencyView 인스턴스를 생성하여 view에 할당한다.
  3. viewDidLoad()에서 self.currencyView.currencyTableView.delegate = self: viewDidLoad()에서는 (1)에서 생성된 currencyView 속성의 테이블 뷰에 델리게이트와 데이터 소스를 설정한다.

결과적으로, 화면에 실제로 보이는 view (2번 인스턴스)와 델리게이트/데이터 소스가 설정된 currencyView 속성 (1번 인스턴스)이 서로 다른 객체였던 것이다. 화면에 보이는 테이블 뷰는 델리게이트와 데이터 소스가 설정되지 않았으므로, cellForRowAt 메서드를 호출할 수 없었던 것이다.

해결책

문제는 ViewControllercurrencyView 속성과 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()
  }
  // ... 나머지 코드
}

이 수정으로 인해 ViewControllercurrencyView 속성과 view 속성이 동일한 CurrencyView 인스턴스를 참조하게 되었고, viewDidLoad()에서 설정한 델리게이트와 데이터 소스가 화면에 보이는 테이블 뷰에 올바르게 적용되어 셀이 정상적으로 표시되었다.

배운 점

  • ViewController에서 view를 커스텀 뷰로 설정할 때는 loadView() 메서드 내에서 view에 할당하는 인스턴스와, ViewController의 다른 속성으로 참조하는 인스턴스가 동일한 객체인지 항상 주의해야 한다.
  • 테이블 뷰 셀이 보이지 않을 때는 데이터 로드 문제뿐만 아니라, 델리게이트/데이터소스 연결, 뷰 계층 구조, 그리고 뷰 컨트롤러의 뷰 초기화 방식까지 폭넓게 디버깅해야 한다.

혹시 이 글을 보시는 분들 중 더 유용한 팁이 있다면 댓글로 많이 알려주세요!

This post is licensed under CC BY 4.0 by the author.