-
할일 리스트(To-Do List) 앱iOS/패스트캠퍼스(앱제작) 2022. 4. 3. 13:33
최종 완성본
< 주요기능 >
1. + 버튼 클릭: 할일 등록
2. 할일 클릭 시 체크 버튼 활성화
3. 스와이프로 delete
4. edit버튼 / delete
5. 할일 순서 재정렬
6. 앱 재실행 시 직전까지 저장한 내용 보여주기 : userDefault
(로컬에 저장한 할일 불러오기)
1. 할일 등록 기능 구현
- 할일 내용과 완료 여부를 저장할 구조체 선언 (별도의 swift파일)
// Task.swift import Foundation struct Task { var title: String // 할일의 내용 저장 var done: Bool // 할일 완료 여부 저장 }
- 등록 버튼 클릭 시 할일이 등록됨
// // ViewController.swift // ToDoList // // Created by YANG on 2022/04/03. // import UIKit class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! // 할일을 저장하는 배열 생성 var tasks = [Task]() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.tableView.dataSource = self } @IBAction func tabEditBtn(_ sender: UIBarButtonItem) { } @IBAction func tabAddBtn(_ sender: UIBarButtonItem) { let alert = UIAlertController(title: "할 일 등록", message: nil, preferredStyle: .alert) // 등록 버튼을 누를때마다 할일 등록 -> handler내에 closure정의 let registerBtn = UIAlertAction(title: "등록", style: .default, handler: { [weak self] _ in // textField에 입력된 값을 가져오기 guard let title = alert.textFields?[0].text else { return } let task = Task(title: title, done: false) self?.tasks.append(task) // 할일이 추가될때마다 테이블뷰 reload -> 할일이 표시되도록 self?.tableView.reloadData() }) let cancleBtn = UIAlertAction(title: "취소", style: .cancel, handler: nil) alert.addAction(cancleBtn) alert.addAction(registerBtn) // closure: alert을 표시하기 전, textfield를 구성(설정)하기 위함. alert.addTextField(configurationHandler: { textField in textField.placeholder = "할 일을 입력해주세요" }) self.present(alert, animated: true, completion: nil); } } 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 return cell } }
2. UserDefault 설정 -> 앱 종료 후 재실행 시 사용자의 정보 load
// 1. 등록한 할일을 userDefault에 저장하는 함수 func saveTasks() { // map을 통해 배열 요소를 딕셔너리 형태로 mapping let data = self.tasks.map { [ "title": $0.title, "done": $0.done ] } let userDefaults = UserDefaults.standard userDefaults.set(data, forKey: "tasks") } // 2. userDefault에 저장된 할일들을 load func loadTasks(){ let userDefaults = UserDefaults.standard guard let data = userDefaults.object(forKey: "tasks") as? [[String: Any]] else { return } 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) } }
- 프로퍼티 옵저버(didSet, willSet)를 통해, tasks배열에 할일(값)이 들어올 때마다 saveTasks()호출 -> userDefault에 값 저장
class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! // 할일을 저장하는 배열 생성 var tasks = [Task]() { didSet { self.saveTasks() } } // 3. 프로퍼티 옵저버 : tasks배열에 할일이 추가될 때마다 userDefault에 할일이 저장됨 override func viewDidLoad() { super.viewDidLoad() self.tableView.dataSource = self self.loadTasks() // 4. userDefault에 저장된 할일 불러오기 }
3. 할일 완료표시 (체크마크) 생성
- tasks배열의 done 프로퍼티에 값 설정 ( false -> true, true -> false)
- delegate를 사용하여 특정 셀에 접근
extension ViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // tasks 인스턴스의 done프로퍼티가 false 이면 true로 var task = self.tasks[indexPath.row] task.done = !task.done self.tasks[indexPath.row] = task // at: 선택된 row만 reload -> 구조체 배열을 받으므로 여러 행 선택 가능 // with: cell을 좌/우로 스왑했을때 나타나는 행동 self.tableView.reloadRows(at: [indexPath], with: .automatic) } }
- dataSource를 사용하여 셀에 체크마크 표시
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 }
4. 할일 수정 기능
- edit버튼 클릭시 done버튼 활성화 / 편집 가능 모드
- done버튼 클릭시 다시 edit버튼 활성화 / 편집 가능 모드 해제override func viewDidLoad() { super.viewDidLoad() self.tableView.dataSource = self self.tableView.delegate = self self.loadTasks() // 4. userDefault에 저장된 할일 불러오기 // done버튼 생성 self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnTap)) } @objc func doneBtnTap() { self.navigationItem.leftBarButtonItem = self.editButton // 편집모드 해제 self.tableView.setEditing(false, animated: true) } @IBAction func tabEditBtn(_ sender: UIBarButtonItem) { guard !self.tasks.isEmpty else { return } // tableCell이 비어있지 않을 때만 편집모드 활성화 되도록 방어코드 self.navigationItem.leftBarButtonItem = self.doneButton // tableCell이 편집모드가 되도록 설정 self.tableView.setEditing(true, animated: true) }
5. 할일 삭제, 재정렬
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) { // 선택된 행의 tasks배열 삭제 self.tasks.remove(at: indexPath.row) // tableView에서도 삭제 tableView.deleteRows(at: [indexPath], with: .automatic) if self.tasks.isEmpty { self.doneBtnTap() } } // 편집모드에서 cell 재정렬 func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { return true } // sourceIndexPath: 원래 있었던 위치, destinationIndexPath: 이동한 위치 func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { // tasks 배열도 재정렬 var tasks = self.tasks let task = tasks[sourceIndexPath.row] tasks.remove(at: sourceIndexPath.row) tasks.insert(task, at: destinationIndexPath.row) self.tasks = tasks }