
내용 정리
개인과제를 Lv 8까지 모두 완료하고, 추가 기능으로 셀을 선택 삭제하는 기능을 구현
이번 추가 구현의 목표는 셀을 여러개 선택하여 삭제할 수 있는 기능을 구현하는 것이다.
어떻게 다중 선택을 구현할까 고민을 해봤는데, 버튼을 하나 만들고 버튼을 클릭하면 뷰의 세팅이 변하며 다중선택을 지원하도록 바뀌는 UI를 만들어야 겠다고 생각했다.
왜냐하면, 셀을 선택했을 때의 액션도 이미 정해져 있고, 실제 연락처 앱에서도 별도의 버튼을 눌러 여러개의 연락처를 동시에 삭제할 수 있도록 설정이 되어있었기 때문이다.
이를 구현하기 위해 뷰 컨트롤러에 새로운 프로퍼티를 선언해준다.
// 뷰의 현재 상태
private var editingMode: Bool = false
이 프로퍼티는 현재 뷰 상태가 편집 상태인지 확인하는 프로퍼티이다.
이제 '편집' 버튼을 만들어 주기로 했다.
편집 버튼은 추가 버튼의 반대편에 위치하게 하고, 전체적으로 추가 버튼과 비슷한 UI를 가지므로 버튼의 UI를 설정하는 메소드를 재활용하여 만들었다.
private let editingButton = UIButton()
/// 에디팅 버튼의 UI를 세팅하는 메소드
func setupEditingButton() {
var config = UIButton.Configuration.plain()
var titleAttr = !self.editingMode ? AttributedString("편집") : AttributedString("취소")
titleAttr.font = .systemFont(ofSize: 20, weight: .medium)
config.attributedTitle = titleAttr
config.baseForegroundColor = !self.editingMode ? .systemBlue : .systemGray
self.editingButton.configuration = config
self.editingButton.backgroundColor = .clear
self.editingButton.addTarget(self, action: #selector(editingTableView), for: .touchUpInside)
}
이제 이 버튼을 눌렀을 때 수행할 액션을 정의해보도록 하자.
editingButton을 눌렀을 때 수행하는 메소드는 아래의 코드이다.
/// 현재 뷰의 모드를 바꾸는 메소드
@objc func editingTableView() {
self.editingMode.toggle()
refreshUI()
view.layoutIfNeeded()
}
/// 버튼의 UI를 새로고침하는 메소드
func refreshUI() {
setupPushButtonView()
setupEditingButton()
self.tableView.reloadData()
}
이 메소드는 편집 버튼을 눌렀을 때 실행되는 메소드로, 현재 뷰 상태를 편집 상태로 변경하고 이에 맞게 UI가 업데이트 되도록 현재 뷰 모드에 따라 UI가 변경되는 뷰 요소들을 다시 세팅하도록 한다.
이렇게 하면 편집 버튼을 눌렀을 때 편집 버튼, 추가 버튼, 테이블 뷰가 다시 세팅되며 편집모드로 변하는 듯한 효과를 줄 수 있다.
사용자에게 시각적으로 선택이라는 효과를 줄 수 있도록 테이블뷰 셀에 새로운 UI를 추가해주었다.
체크박스는 SFSymbols에서 가져왔으며UIButton으로 구현했다.
private let checkBox = UIButton()
func setupCheckBox() {
self.checkBox.isSelected = self.isSelected ? true : false
self.checkBox.backgroundColor = .clear
self.checkBox.setImage(UIImage(systemName: "checkmark.circle"), for: .normal)
self.checkBox.setImage(UIImage(systemName: "checkmark.circle.fill"), for: .selected)
self.checkBox.imageView?.tintColor = .systemBlue
self.checkBox.addTarget(self, action: #selector(checkBoxToggle), for: .touchDown)
}
@objc func checkBoxToggle() {
self.checkBox.isSelected.toggle()
self.isSelected.toggle() // 셀이 선택된 것과 같은 효과
}
버튼이 선택되었을 때와 선택되지 않았을 때의 모양을 다르게 하고, 버튼을 선택하면 상태가 변하도록 구현하였다.

여기서부터가 오늘의 진짜 과제이다.
선택한 셀의 데이터만 삭제하는 것을 어떻게 구현하면 좋을까...
열심히 고민한 결과 셀의 선택을 감지하는 델리게이트 메소드를 활용해보기로 했다.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
위의 메소드는 셀이 선택되었을 때 어떤 동작을 실행할건지 설정해줄 수 있다.
난 여기서 editingMode(현재 뷰 모드)가 true인지 false인지 확인하여 각각 다른 동작을 구현해 주었다.
만약 false라면 연락처 수정을 할 수 있도록 하고, true라면 삭제할 데이터를 모아두는 배열에 삽입하도록 구현했다.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 뷰 모드가 노말이라면 연락처 수정 기능
// 뷰 모드가 편집이라면 셀 선택
if !self.editingMode {
let data = self.dataSource[indexPath.row]
guard let name = data.name, let number = data.number, let imageData = data.profile else { return }
guard let image = UIImage(data: imageData) else { return }
let phoneNumber = number.split(separator: "-")
// 서브뷰 업데이트 메소드 추가
let destinationView = PhoneBookViewController()
destinationView.editPhoneNumber(name: name, number: phoneNumber.joined(), image: image)
destinationView.state = .edit
self.navigationController?.navigationBar.isHidden = false
self.navigationController?.pushViewController(destinationView, animated: true)
} else {
guard !self.selectedItem.contains(self.dataSource[indexPath.row]) else {
self.selectedItem.remove(self.dataSource[indexPath.row])
return
}
self.selectedItem.insert(self.dataSource[indexPath.row])
}
}
여기서 선택한 셀을 한 번 더 선택했을 경우 선택에서 제외하는 것으로 했다.
selectedItem은 삭제하려고 하는 데이터를 모아두는 배열로 Set 으로 구현했다.
이제 선택된 셀이 있을 때, '지우기' 버튼을 누르면 선택한 셀이 모두 삭제되도록 해야한다.
이를 위해 새로운 메소드를 하나 만들어준다.
/// 선택된 테이블뷰 셀을 삭제하는 메소드
/// 선택된 셀이 없을 경우 전체 삭제
func deleteTableViewCell() {
if !self.selectedItem.isEmpty {
ValidationAlert.confirmDeleteDataAlert(on: self) {
self.selectedItem.forEach {
guard let name = $0.name, let number = $0.number else { return }
self.deleteData(name: name, number: number)
}
self.updateTableViewData()
self.selectedItem.removeAll()
self.tableView.reloadData()
self.view.layoutIfNeeded()
}
} else {
guard !self.dataSource.isEmpty else {
ValidationAlert.showValidationAlert(on: self, title: "경고", message: "삭제할 데이터가 없습니다!!")
return
}
ValidationAlert.confirmDeleteDataAlert(on: self) {
self.deleteAllData()
self.updateTableViewData()
self.selectedItem.removeAll()
self.tableView.reloadData()
self.view.layoutIfNeeded()
}
}
}
이 때 만약 선택된 셀이 하나도 없는 상태에서 '지우기' 버튼을 누른다면 모든 데이터를 삭제하는 옵션을 실행하도록 한다.
다만, 데이터가 존재하지 않는데 모두 지우려고 한다면 데이터가 존재하지 않는다는 경고를 Alert으로 알려준다.

오늘은 개인과제에서 추가 구현에 대해 고민하고 진행해보며 여러 난항을 겪었다.
그 중 가장 어려웠던 셀 선택 삭제에 대해 간단히 블로그를 작성해 보았다.
사실 아직 완벽하지 않은 기능이라 리팩토링이 더 필요하기 때문에 내일 이어서 만들어 보려고 한다.
캬아 alert 하고 싶었는데 시간 부족해서 못했는데.. 과제를 모두 완료하고 남는 시간에 하시다니 게다가 삭제도 여러 방법으로 가능하게 하신 부분이 인상 깊네요