프로젝트를 진행하면서 직군을 선택하기 위한 기능을 만들었는데, 이를 공유하고자 합니다.
class ViewController: UIViewController {
// 원하는 아이템을 넣어주면 됩니다.
private var items = []
// 검색 결과를 담는 배열
private var filteredItems: [String] = []
// checkButton 선택 셀 index
private var previousIndexPath: IndexPath?
private var selectedIndexPath: IndexPath?
// searchBar 설정
private lazy var searchBar : UISearchBar = {
let search = UISearchBar()
search.delegate = self
search.searchBarStyle = .minimal
search.showsCancelButton = true
search.searchTextField.backgroundColor = .clear
search.searchTextField.borderStyle = .none
return search
}()
// 직무를 보여줄 tableView
private let tableView : UITableView = {
let tableView = UITableView()
tableView.separatorStyle = .none
return tableView
}()
}
그 다음 viewdidload 에서 TableView와 searchBar를 설정합니다. (레이아웃 코드는 제거했습니다)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.configureTableView()
self.reload()
}
private func configureTableView() {
tableView.dataSource = self
tableView.delegate = self
// "NameCell" 이라는 이름의 커스텀 셀을 사용했습니다.
tableView.register(NameCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(tableView)
}
private func reload() {
self.tableView.reloadData()
}
UISearchBarDelegate를 채택하여 필요한 메서드들을 구현해줍니다.
extension ViewController: UISearchBarDelegate {
// 유저가 텍스트 입력했을 때
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterItems(with: searchText)
self.reload()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
// 취소 버튼을 누를 때 검색어를 초기화하고 테이블 뷰를 갱신합니다.
searchBar.text = nil
searchBar.resignFirstResponder() // 키보드 내림
filterItems(with: "")
self.reload()
}
private func filterItems(with searchText: String) {
if searchText.isEmpty {
// 검색어가 비어있으면 모든 항목을 포함
filteredItems = items
} else {
// 검색어를 기준으로 items 배열을 필터링하여 검색 결과를 filteredItems에 저장
filteredItems = items.filter { $0.range(of: searchText, options: .caseInsensitive) != nil }
}
}
}
TableView를 관리하는데 필요한 protocol들을 채택하여 필요한 메서드들을 구현해줍니다.
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
let sectionTitles = getSectionTitles()
return sectionTitles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let filteredItemsInSection = getFilteredItemsInSection(section)
return filteredItemsInSection.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NameCell
let filteredItemsInSection = getFilteredItemsInSection(indexPath.section)
let item = filteredItemsInSection[indexPath.row]
cell.textLabel?.text = item
if let selectedIndexPath = selectedIndexPath, selectedIndexPath == indexPath {
cell.checkButton.isHidden = false
} else {
cell.checkButton.isHidden = true
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let previousIndexPath = selectedIndexPath // 이전에 선택된 셀의 인덱스 저장
selectedIndexPath = indexPath // 선택된 셀의 인덱스 업데이트
// 이전에 선택된 셀의 인덱스와 현재 선택한 셀의 인덱스가 같으면 체크 버튼을 숨깁니다.
if previousIndexPath == indexPath {
if let cell = tableView.cellForRow(at: indexPath) as? NameCell {
cell.checkButton.isHidden = true
}
selectedIndexPath = nil // 선택된 셀의 인덱스를 nil로 설정하여 선택 해제
} else {
// 이전에 선택된 셀의 인덱스와 현재 선택한 셀의 인덱스가 다르면 이전에 선택된 셀을 업데이트합니다.
if let previousIndexPath = previousIndexPath, let cell = tableView.cellForRow(at: previousIndexPath) as? NameCell {
cell.checkButton.isHidden = true
}
if let cell = tableView.cellForRow(at: indexPath) as? NameCell {
cell.checkButton.isHidden = false
}
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// 섹션 헤더에 표시할 문자열을 반환합니다.
let sectionTitles = getSectionTitles()
if section < sectionTitles.count {
return sectionTitles[section]
}
return nil
}
private func searchBarIsEmpty() -> Bool {
return searchBar.text?.isEmpty ?? true
}
private func getSectionTitles() -> [String] {
// 이름의 첫 글자로 이루어진 섹션 타이틀 배열을 반환합니다.
let sectionTitles = items.map { name -> String in
if let firstCharacter = name.first, let unicodeScalar = firstCharacter.unicodeScalars.first {
let scalarValue = unicodeScalar.value
if (0xAC00 <= scalarValue && scalarValue <= 0xD7A3) { // 첫 글자가 한글인 경우
let unicodeValue = scalarValue - 0xAC00
let choseongIndex = Int(unicodeValue / (21 * 28))
let choseong = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"]
let choseongCharacter = choseong[choseongIndex]
return choseongCharacter
} else { // 첫 글자가 한글이 아닌 경우
return name.prefix(1).uppercased()
}
} else { // 이름이 비어있는 경우
return ""
}
}
let uniqueTitles = Array(Set(sectionTitles)).sorted()
return uniqueTitles
}
private func getFilteredItemsInSection(_ section: Int) -> [String] {
let sectionTitles = getSectionTitles()
let sectionTitle = sectionTitles[section]
let filteredItemsInSection: [String]
if searchBarIsEmpty() {
filteredItemsInSection = items.filter { item -> Bool in
if let firstCharacter = item.first, let unicodeScalar = firstCharacter.unicodeScalars.first {
let scalarValue = unicodeScalar.value
if (0xAC00 <= scalarValue && scalarValue <= 0xD7A3) { // 첫 글자가 한글인 경우
let unicodeValue = scalarValue - 0xAC00
let choseongIndex = Int(unicodeValue / (21 * 28))
let choseong = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"]
let choseongCharacter = choseong[choseongIndex]
return "\(choseongCharacter)" == sectionTitle
} else { // 첫 글자가 한글이 아닌 경우
return item.prefix(1).uppercased() == sectionTitle
}
} else { // 이름이 비어있는 경우
return sectionTitle.isEmpty
}
}
} else {
filteredItemsInSection = filteredItems.filter { item -> Bool in
if let firstCharacter = item.first, let unicodeScalar = firstCharacter.unicodeScalars.first {
let scalarValue = unicodeScalar.value
if (0xAC00 <= scalarValue && scalarValue <= 0xD7A3) { // 첫 글자가 한글인 경우
let unicodeValue = scalarValue - 0xAC00
let choseongIndex = Int(unicodeValue / (21 * 28))
let choseong = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"]
let choseongCharacter = choseong[choseongIndex]
return "\(choseongCharacter)" == sectionTitle
} else { // 첫 글자가 한글이 아닌 경우
return item.prefix(1).uppercased() == sectionTitle
}
} else { // 이름이 비어있는 경우
return sectionTitle.isEmpty
}
}
}
return filteredItemsInSection
}
}