[Swift] UITableView Section 나누기

술술·2024년 3월 26일

TIL

목록 보기
12/21

UITableView Section 나누기

날짜별로 섹션을 나누고 싶었는데, 섹션 개수를 나누는 건 쉬웠다.


  func numberOfSections(in tableView: UITableView) -> Int {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM월 dd일"
    
    // dueDate를 날짜 형식에 맞게 바꿈. 그리고 중복 없애서 배열에 넣기
    let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
    
    return dueDates.count
  }

근데 이제 문제는 섹션별(날짜별)로 그에 해당하는 데이터만큼 행의 개수를 설정하는 것.
예를 들어 3월 27일에 데이터가 2개다 --> 3월 27일 섹션에 행 2개
내가 원한 건 이런 거였는데, 일단 섹션타이틀이 자꾸 3월 27일이 2개 나왔다. 내가 원한 건 3월 26, 27, 28일 하나씩 나오는거였는데..🥲

일단 섹션 타이틀 부분의 코드는 이렇다.

  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM월 dd일"
    
    let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
    
    return dueDates[section]
  }

GPT랑도 싸우고 블로그를 뒤져본 결과 탄생한 코드...

처음에 GPT는 Calendar.current.isDate() 함수를 자꾸 알려줬다.
그래서 갑자기 무슨 함수인지 공부했다.
두 날짜가 동일한 날짜인지 비교해주는 함수였다.
GPT는 이 함수를 이용해서 동일한 날짜인 것만 남겨서 그걸 섹션타이틀로 만들자는 것이었다. 하지만 이 방법은 잘 통하지 않았고..

let calendar = Calendar.current
let date1 = Date() // 현재 날짜와 시간
let date2 = Date(timeIntervalSinceNow: 3600) // 현재로부터 1시간 후의 날짜와 시간

if calendar.isDate(date1, inSameDayAs: date2) {
    print("두 날짜는 동일한 날짜입니다.")
} else {
    print("두 날짜는 서로 다른 날짜입니다.")
}

나는 https://gigas-blog.tistory.com/46 이 블로그를 참고해서 해결했다.



다음 문제는 섹션별(날짜별)로 그에 해당하는 데이터만큼 행의 개수를 설정하는 것.
3월 28일의 데이터는 1개인데, 자꾸 2개의 행이 만들어졌다.

이 문제도 아까 참고했다고 한 블로그에서 도움을 받았다.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM월 dd일"
    
    let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
    
    // titleForHeaderInSection
    let sectionTitle = dueDates[section]
    
    return toDo.filter { dateFormatter.string(from: $0.dueDate) == sectionTitle }.count
  }

다음 단계는 섹션별로 알맞은 데이터를 보여주는 것.

이 것은 쉽게 했는데, 다른 문제가 있었다.
스위치가 첫번째 섹션만 동작하고, 다른 섹션은 동작하지 않았다.
프린트 찍어서 스위치가 눌리기는 하는건지도 알아보고, 블로그도 찾아보고, 지피티도 찾아보고 결국 해결!


// @objc func switchValueChanged(_ sender: UISwitch)
// switch의 addTarget으로 들어가는 함수

let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
let sectionTitle = dueDates[indexPath.section]
let toDoByDate = toDo.filter { dateFormatter.string(from: $0.dueDate) == sectionTitle }
    
if let index = toDo.firstIndex(where: { dateFormatter.string(from: $0.dueDate) == sectionTitle && $0.title == toDoByDate[indexPath.row].title }) {
    toDo[index].isComplete = sender.isOn
}

원래는 dateTodo만 수정하고 있었는데, dateTodo만 수정하는 건 해당 섹션에 표시되는 할 일 목록만 포함하기 때문에, toDo 데이터를 수정하지 않고 있었다.

  • toDo.firstIndex(where:): 배열에서 특정 조건을 만족하는 첫 번째 요소의 인덱스를 찾는다. 여기서는 where 절에서 dateFormatter.string(from: $0.dueDate) == sectionDate와 $0.title == dateTodo[indexPath.row].title의 조건을 만족하는 인덱스를 찾는다.
  • dateFormatter.string(from: $0.dueDate) == sectionDate: 선택된 섹션에 해당하는 날짜와 같고,
  • $0.title == dateTodo[indexPath.row].title: 선택된 섹션에서 선택된 행에 해당하는 할 일의 제목과 같은 경우,
  • toDo[index].isComplete = sender.isOn: 찾은 인덱스에 해당하는 할 일의 isComplete 속성을 스위치의 상태에 따라 업데이트한다.


섹션과 관련된 부분의 전체 코드

// MARK: - Datasource, Delegate
extension ViewController: UITableViewDataSource, UITableViewDelegate {
  
  // Section
  func numberOfSections(in tableView: UITableView) -> Int {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM월 dd일"
    
    // dueDate를 날짜 형식에 맞게 바꿈. 그리고 중복 없애서 배열에 넣기
    let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
    
    return dueDates.count
  }
  
  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM월 dd일"
    
    let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
    
    return dueDates[section]
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "MM월 dd일"
    
    let dueDates = Array(Set(toDo.map { dateFormatter.string(from: $0.dueDate) })).sorted()
    
    // titleForHeaderInSection
    let sectionTitle = dueDates[section]
    
    return toDo.filter { dateFormatter.string(from: $0.dueDate) == sectionTitle }.count
  }
profile
Hello

0개의 댓글