TodoList

Soey·2022년 5월 11일
0

Swift

목록 보기
7/7

Tric : 작성값을 배열에 저장, 완료표시, 제거, UserDefalut로 유지

📱Story Board

navigation controller 생성 root VC 설정 후 bar button item의 system item 을 edit으로!

table view cell의 속성 영역 → style(custom, basic . .) 설정하여 원하는 기입 방식 설정

🧑‍💻Code

  1. UIAlertController & UIAlertAction로 할 일을 입력 받는다. (구조체 미리 만들고 정의)
@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)
    }
}
  • debugPrint는 print가 Hello 만 출력하는 것과 다르게 “Hello” 와 같이 상세하게 출력해준다.
  1. 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에서 필수 이다.
  1. 각 배열을 Dictionary 형태로 UserDefault <구조 눈에 익히기>
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 배열의 요소들을 딕셔너리형태로 저장
    }
  • 배열의 첫번째 인덱스(map이니까 해당 인덱스겠지) - 선택된 배열($0)의 key들 (title, done)을 딕셔너리형태로 저장
  • print( "(data)" ) → [["title": "춤추기", "done": false], ["title": "공부하기", "done": false], ["title": "양치하기", "done": false]] 딕셔너리 형태로 출력
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)
        })
        // 딕셔너리 배열 형태로 타입캐스팅
    }
  1. 어떤 셀이 선택되었는지 Delegate
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이 선택되었는 지 알기 위함
  1. 선택된 셀의 Done은 Bool값이 바뀐다 (True & False ) 이를 accensary로 표현
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를 전달
    }
  1. Edit 버튼 눌렀을 때, 편집화면으로 바뀌는 것 구현
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)
				// 편집 모드로 들어가는 방법
    }
  1. Done버튼 눌렀을때, 편집모드 빠져나오는 것
@objc func doneButtonTap() {
        self.navigationItem.leftBarButtonItem = self.editButton
        self.tableView.setEditing(false, animated: true)
    }
  1. 마이너스 버튼을 누르면 할일이 테이블 뷰에서 삭제되도록

    → 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()
        }
    } // 편집모드에서 선택된 셀 알려주는 메소드
.
.
}
  1. 편집모드에서 할일의 순서를 변경하기
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) 셀 오른쪽에 재정렬 버튼이 생성되며 이를 통해 셀의 순서를 바꿀 수 있다.

profile
iOS Developer

0개의 댓글