Tric : 작성값을 배열에 저장, 완료표시, 제거, UserDefalut로 유지
navigation controller 생성 root VC 설정 후 bar button item의 system item 을 edit으로!
table view cell의 속성 영역 → style(custom, basic . .) 설정하여 원하는 기입 방식 설정
@IBAction func tapAddButton(_ sender: Any) {
let alert = UIAlertController(title: "할 일 등록", 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?.tableView.reloadData()
// Alert의 텍스트필드에 기입할때마다 tasks 배열에 저장 됨
})
let cancelButton = UIAlertAction(title:"취소", style: .cancel , handler: nil)
// 취소 버튼 눌렀을때 아무것도 실행하지 않는 것을 클로저에 정의
// ----------------------------------
alert.addAction(registerButton)
alert.addAction(cancelButton)
alert.addTextField(configurationHandler: {textField in
textField.placeholder = "한 일을 등록해주세요"
})
// 클로저를 받고 있다.
// Alert 표시 이전 텍스트필드를 구성하기 위한 클로저 알럿에 표시할 텍스트 필드를 나타내는 클로저
self.present(alert, animated: true, completion: nil)
}
}
UITableViewDataSource를 정의하여 텍스트 필드로 작성한 값을 TableView에 출력시킨다.
→ Tabel View Cell의 행의 갯수와 행의 특성을 정의하기 위해 채택해야 함.
class ViewController: UIViewController {
var tasks = [Task]() // 넣을 할일 구조체들을 여기에 정의.
.
.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self // dataSource 프로토콜을 VC에 채택
}
.
.
.
} //class 바깥에서 !
.
.
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
//바로 위 cellForRowAt의 파라미터인 indexPath를 전달
} // dequeueReusableCell : 위에 numberOfRowsInSection에서 return한 즉, 내가 사용한 만큼만 셀이 재사용되어 메모리 누수 방지
} // 옵셔널 붙지않는 위의 두 함수는 UITableViewDataSource에서 필수 이다.
func saveTasks() {
let data = self.tasks.map {
["title": $0.title,
"done": $0.done
]
}
let userDefaluts = UserDefaults.standard
userDefaluts.set(data, forKey: "tasks")
// value, key 값을 parameter로 각각 저장
// task 배열의 요소들을 딕셔너리형태로 저장
}
func loadTasks() {
let userDefaluts = UserDefaults.standard
guard let data = userDefaluts.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 }
// done 키로 딕셔너리 value를 가져오는 과정
return Task(title: title, done: done)
})
// 딕셔너리 배열 형태로 타입캐스팅
}
override func viewDidLoad() {
.
.
self.tableView.delegate = self
.
.
}
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
// 변경된 값(Done이 체크된거)을 원래 task에 덮어씌워 줌
self.tableView.reloadRows(at: [indexPath], with: .automatic)
// 눌린 행만 reload되게 ! with는 애니메이션 ㅎㅎ
//debugPrint([indexPath]) -> 눌린 테이블을 전달하는 인자 [0]으로 전달
//debugPrint([indexPath.row]) -> 눌린 테이블이 테이블뷰에서 몇번째 인지!
}
} // 어떤 Cell이 선택되었는 지 알기 위함
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 = .checkmark
}
return cell
//바로 위 cellForRowAt의 파라미터인 indexPath를 전달
}
override func viewDidLoad() {
super.viewDidLoad()
self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTap))
// 위에서 정의한 doneButton 생성 버튼 눌러질때만(동적호출용도) 사용되는 메소드
// doneButton이 눌리면 #selector 하겠다~
self.tableView.dataSource = self
self.tableView.delegate = self
self.loadTasks()
}
@objc func doneButtonTap() {
}
@IBAction func tapEditButton(_ sender: Any) {
guard !self.tasks.isEmpty else { return }
// tasks 배열이 비어있지 '않을' 때만 실행하겠다
self.navigationItem.leftBarButtonItem = self.doneButton
// 위에서 생성한 doneButton을 이제야 사용!
self.tableView.setEditing(true, animated: true)
// 편집 모드로 들어가는 방법
}
@objc func doneButtonTap() {
self.navigationItem.leftBarButtonItem = self.editButton
self.tableView.setEditing(false, animated: true)
}
마이너스 버튼을 누르면 할일이 테이블 뷰에서 삭제되도록
→ tableView commit editingStyle : 편집모드에서 마이너스 버튼이 눌렸을 때 그것이 어떤 버튼인지 알려주는 메소드 ( 다 삭제되면 편집모드에서 빠져나오기 ! )
extension ViewController: UITableViewDataSource {
.
.
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
{
self.tasks.remove(at: indexPath.row)
// tasks 배열에서 삭제
tableView.deleteRows(at: [indexPath], with: .automatic )
// tasks 배열에서 삭제된 것이 tableView에도 적용
if self.tasks.isEmpty {
self.doneButtonTap()
}
} // 편집모드에서 선택된 셀 알려주는 메소드
.
.
}
extension ViewController: UITableViewDataSource {
.
.
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
} // 변경된 셀을 재정렬하는 메소드
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 // 위에 변경된 tasks 배열을 tasks에 적용
}
.
.
}
(1) Add버튼 → textField에 할일 입력.
(2) Task 구조체에 삽입 → UITableViewDataSource(템플릿)를 통해 Task가 cell.textLabel?.text = task.title 로써 화면에 출력 → reloadData를 통해 (+) 누를때마다 넣을 수 있게끔 함.
(3) 각 배열을 Dictionary형태로 UserDefalut에 저장
(4) Delegate를 통해 체크된 셀Done의 Bool 값을 바꾼다.
(5) cell.accesary로 done이 바뀐 셀을 커스텀한다.
(6) Edit 버튼 눌렀을 때 편집화면으로 이동하며, Edit 버튼이 Done으로 바뀐다.
(7) Done 버튼 누르면 다시 Edit화면으로 이동하며, Done 버튼이 Edit 버튼으로 바뀐다.
(8) 편집화면에서 (-) 버튼을 누르면 할 일이 삭제된다.
(9) 셀 오른쪽에 재정렬 버튼이 생성되며 이를 통해 셀의 순서를 바꿀 수 있다.