iOS/패스트캠퍼스(앱제작)
할일 리스트(To-Do List) 앱
soultreemk
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
}