1. 기초 UI 디자인
2. 코드작업
@IBAction func tabAddButton(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "MustDo", message: nil, preferredStyle: .alert)
let registerButton = UIAlertAction(title: "등록", style: .default, handler: { [weak self] _ in
guard let title = alert.textFields?[0].text else { return }
let task = Task(title: title, done: false)
self?.tasks.append(task)
self?.mustDoTableView.reloadData()
})
let cancelButton = UIAlertAction(title: "취소", style: .cancel, handler: nil)
alert.addAction(cancelButton)
alert.addAction(registerButton)
alert.addTextField(configurationHandler: { textField in
textField.placeholder = "할 일을 입력해주세요."
})
self.present(alert, animated: true, completion: nil)
}
- Alert은 iOS에서 보통 앱 또는 기기의 상태와 관련된 중요한 정보를 전달하며, 종종 사용자에게 피드백을 요청하기 위해 사용됩니다. UIAlertController()를 통해 생성할 수 있고, UIAlertAction()을 통해 Alert을 구성하는 버튼을 생성할 수 있습니다. UIAlertAction()에는 handler라는 Parameter가 있는데 버튼이 눌릴 때 이 곳에 정의된 클로저가 호출됩니다.
- closure는 참조타입이기 때문에 closure의 본문에서 self로 인스턴스를 캡쳐할 때 강한 순환 참조가 발생할 수있습니다. 때문에 클로저-클래스인스턴스 사이의 강한순환참조를 방지하기 위해 [weak self] 캡쳐목록을 정의하였습니다.
UITableViewDataSource는 테이블 뷰를 생성하고 수정하는데 필요한 정보를 테이블 뷰 객체에 제공합니다.
이때, 옵셔널이 아닌 메서드는 필수로 구현해줘야 합니다.
UITableViewDelegate는 테이블 뷰의 시각적인 부분을 설정하고, 행의 액션 관리, 악세서리 뷰 지원 그리고 테이블 뷰의 개별 행 편집을 도와줍니다.
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let task = self.tasks[indexPath.row]
cell.textLabel?.text = task.title
if task.done {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
self.tasks.remove(at: indexPath.row)
self.mustDoTableView.deleteRows(at: [indexPath], with: .automatic)
if self.tasks.isEmpty {
self.tabDoneButton()
}
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
var tasks = self.tasks
let task = tasks[sourceIndexPath.row]
tasks.remove(at: sourceIndexPath.row)
tasks.insert(task, at: destinationIndexPath.row)
self.tasks = tasks
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var task = self.tasks[indexPath.row]
task.done = !task.done
self.tasks[indexPath.row] = task
self.mustDoTableView.reloadRows(at: [indexPath], with: .automatic)
}
}
func saveTasks() {
let data = self.tasks.map {
[
"title": $0.title,
"done": $0.done
]
}
let userDefaults = UserDefaults.standard
userDefaults.set(data, forKey: "tasks")
}
func loadTasks() {
let userDefaults = UserDefaults.standard
guard let data = userDefaults.object(forKey: "tasks") as? [[String: Any]] else { return }
// "tasks"에 대한 value가 Any?타입으로 반환되는데, 이 Any를 [[String: Any]] 로 타입캐스팅한 것.
self.tasks = data.compactMap {
guard let title = $0["title"] as? String else { return nil }
guard let done = $0["done"] as? Bool else { return nil }
return Task(title: title, done: done)
}
}