[iOS] TableView, SearchBar 이용하여 검색 기능 만들기

David·2023년 7월 14일
0

기능 구현

목록 보기
1/1
post-thumbnail

프로젝트를 진행하면서 직군을 선택하기 위한 기능을 만들었는데, 이를 공유하고자 합니다.

Preview

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
    }
    
    
}

0개의 댓글