관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UITableView Multiple Cell (다중 셀) 처리 방법 (다중 Section vs 단일 Section) 본문

iOS 응용 (swift)

[iOS - swift] UITableView Multiple Cell (다중 셀) 처리 방법 (다중 Section vs 단일 Section)

jake-kim 2022. 3. 6. 15:23

다중 Section

Section을 사용하는 기준

  • 분류 - TableView / Section / Cell
    • -> Cell들은 서로 연관되어 있는지? 연관이 적으면 Section으로 나누기
    • -> Section들은 표현하려는 방향이 같은지? 표현하려는 방향이 같으면 하나의 TableView에 표현

Section을 한 개만 사용 vs Section을 여러개 사용

  • 아래처럼 아이폰에 설치되어 있는 Setting앱을 보면, 하나의 TableView안에 Section이 여러개로 나누어져 있는 패턴

  • Section의 개념?
    • Section은 하나의 TableView 안에서 데이터의 성격을 분류할 때 사용하는 것
  • Cell은 같은것을 쓸지라도 성격에 따라 Section을 나누어서 사용할 것 (Cell만 적용하다가 Cell끼리의 성격이 달라진다 싶으면 바로 Section으로 나눌 것)
  • Section을 나누는것에 집중할때, 확장성에 이점이 존재
    • 위 처럼 성격이 다른 아이템 간 거리 간격을 띄워달라는 기획 요청사항이 생긴 경우 -> 애초부터 Section을 여러개로 쪼개어 개발된 것이 손쉽게 수정 가능

Section을 한 개만 사용하여 다중 셀 구현 방법

1-section, n-cell

  • Model 정의
    • 1-section, n-cell을 사용할 땐 모델쪽에서 Mode같은것을 두어서 Cell을 구분
    • 아래처럼 enum으로 구분도 가능
// OnlyCellModel.swift
enum OnlyCellModel {
  case profile(profileImage: UIImage?, titleText: String, descriptionText: String)
  case option(iconImage: UIImage?, title: String)
  case title(title: String)
}
  • 사용할 Cell 세 가지 준비 

  • ViewController - TableView에 Cell 등록, dataSource 선언
  //  ViewController.swift
  private let settingTableView = UITableView(frame: .zero, style: .insetGrouped).then {
    $0.register(ProfileCell.self, forCellReuseIdentifier: ProfileCell.id)
    $0.register(TitleCell.self, forCellReuseIdentifier: TitleCell.id)
    $0.register(OptionCell.self, forCellReuseIdentifier: OptionCell.id)
    $0.separatorStyle = .none
  }
  
  private var dataSource = [OnlyCellModel]()
  • ViewControlelr - data 로드
  private func refresh() {
    self.dataSource = [
      .profile(
        profileImage: UIImage(named: "person"),
        titleText: "Jake Kim",
        descriptionText: "Apple ID, iCloud, 미디어 및 구입"
      ),
      .title(title: "iPhone 설정 마저 하기"),
      .option(iconImage: UIImage(systemName: "pencil.fill"), title: "수정하기"),
      .option(iconImage: UIImage(systemName: "square.and.arrow.up.fill"), title: "공유"),
      .option(iconImage: UIImage(systemName: "paperplane.fill"), title: "네비게이션")
    ]
    self.settingTableView.reloadData()
  }
  • ViewController - dataSource
extension ViewController: UITableViewDataSource {
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    self.dataSource.count
  }
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    switch self.dataSource[indexPath.row] {
    case let .profile(profileImage, titleText, descriptionText):
      let cell = tableView.dequeueReusableCell(withIdentifier: ProfileCell.id, for: indexPath) as! ProfileCell
      cell.prepare(profileImage: profileImage, titleText: titleText, supplementText: descriptionText)
      return cell
    case let .title(title):
      let cell = tableView.dequeueReusableCell(withIdentifier: TitleCell.id, for: indexPath) as! TitleCell
      cell.prepare(titleText: title)
      return cell
    case let .option(iconImage, title):
      let cell = tableView.dequeueReusableCell(withIdentifier: OptionCell.id, for: indexPath) as! OptionCell
      cell.prepare(icon: iconImage, titleText: title)
      return cell
    }
  }
}

* 1-section, n-cell 전체 코드: https://github.com/JK0369/ExTableViewMultiSection

Section을 여러개 사용하여 구현 방법

n-section, n-cell

  • Section을 여러개 사용할 것이기 때문에 enum으로 Section 정의
    • associated-type으로 Cell의 데이터형을 배열로 정의 (Cell이 여러개이므로 배열로 정의)
// SettingSection.swift

enum SettingSection {
  case profile([ProfileModel])
  case option([OptionModel])
  case title([TitleModel])
}

struct ProfileModel {
  let profileImage: UIImage?
  let titleText: String?
  let descriptionText: String?
}

struct OptionModel {
  let iconImage: UIImage?
  let title: String?
}

struct TitleModel {
  let title: String?
}
  • ViewController - 데이터 로드
private func refresh() {
  // profile
  let profileModel = ProfileModel(
    profileImage: UIImage(named: "person"),
    titleText: "Jake Kim",
    descriptionText: "Apple ID, iCloud, 미디어 및 구입"
  )
  let profileSection = SettingSection.profile([profileModel])
  
  // title
  let titleModel = TitleModel(title: "iPhone 설정 마저 하기")
  let titleSection = SettingSection.title([titleModel])
  
  // option
  let optionModels = [
    OptionModel(iconImage: UIImage(systemName: "pencil"), title: "수정하기"),
    OptionModel(iconImage: UIImage(systemName: "square.and.arrow.up.fill"), title: "공유"),
    OptionModel(iconImage: UIImage(systemName: "paperplane.fill"), title: "네비게이션")
  ]
  let optionSection = SettingSection.option(optionModels)
  
  self.dataSource = [profileSection, titleSection, optionSection]
  self.settingTableView.reloadData()
}
  • ViewController - dataSource
extension ViewController: UITableViewDataSource {
  func numberOfSections(in tableView: UITableView) -> Int {
    self.dataSource.count
  }
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    switch self.dataSource[section] {
    case let .profile(profileModels):
      return profileModels.count
    case let .title(titleModels):
      return titleModels.count
    case let .option(optionModels):
      return optionModels.count
    }
  }
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    switch self.dataSource[indexPath.section] {
    case let .profile(profileModels):
      let cell = tableView.dequeueReusableCell(withIdentifier: ProfileCell.id, for: indexPath) as! ProfileCell
      let profileModel = profileModels[indexPath.row]
      cell.prepare(
        profileImage: profileModel.profileImage,
        titleText: profileModel.titleText,
        supplementText: profileModel.descriptionText
      )
      return cell
    case let .title(titleModels):
      let cell = tableView.dequeueReusableCell(withIdentifier: TitleCell.id, for: indexPath) as! TitleCell
      let titleModel = titleModels[indexPath.row]
      cell.prepare(titleText: titleModel.title)
      return cell
    case let .option(optionModels):
      let cell = tableView.dequeueReusableCell(withIdentifier: OptionCell.id, for: indexPath) as! OptionCell
      let optionModel = optionModels[indexPath.row]
      cell.prepare(
        icon: optionModel.iconImage,
        titleText: optionModel.title
      )
      return cell
    }
  }
}

* n-section, n-cell 전체 코드: https://github.com/JK0369/ExTableViewMultiSection/tree/MultiSection

번외) Section 스타일

* 이전 포스팅 글 참고: https://ios-development.tistory.com/538

Comments