Potato
์•ˆ๋…•ํ•˜์„ธ์š”, ๊ฐ์žก๋‹ˆ๋‹ค?๐Ÿฅ” ^___^ ๐Ÿ˜บ github ๋ฐ”๋กœ๊ฐ€๊ธฐ ๐Ÿ‘‰๐Ÿป

potato's iOS Story/CloneApp + ์ฝ”๋“œ๋ฆฌ๋ทฐ

[iOS] Swift๋กœ ์•„์ฃผ ๊ฐ„๋‹จํ•œ Todo List ๋งŒ๋“ค๊ธฐ (1)

๊ฐ์ž ๐Ÿฅ” 2022. 2. 4. 18:29
๋ฐ˜์‘ํ˜•

 

์•ˆ๋…•ํ•˜์„ธ์š”, ๊ฐ์ž์ž…๋‹ˆ๋‹ค. ์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” ํŒจ์ŠคํŠธ ์บ ํผ์Šค ๊ฐ•์˜๋ฅผ ๋ณด๋ฉด์„œ ๋”ฐ๋ผํ•ด๋ณธ (ํด๋ก ์ฝ”๋”ฉ) ๊ฐ„๋‹จํ•œ Todo List๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋Š” ์ฝ”๋“œ์— ๋Œ€ํ•ด์„œ ๋ฆฌ๋ทฐ๋ฅผ ํ•ด๋ณด๋ ค๊ณ  ํ•ด์š”. ์ฒ˜์Œ ๋ฐฐ์šฐ๋Š” ์•ฑ๊ฐœ๋ฐœ์ด๋‹ค๋ณด๋‹ˆ, ํด๋ก ์ฝ”๋”ฉ์„ ํ•˜๋ฉด์„œ๋„ ๋ชจ๋ฅด๋Š” ๋ถ€๋ถ„์ด ์กฐ๊ธˆ ์žˆ๋”๋ผ๊ตฌ์š”. ๊ทธ๋ž˜์„œ ํ”„๋กœ์ ํŠธ๊ฐ€ ํ•˜๋‚˜์”ฉ ๋๋‚ ๋•Œ๋งˆ๋‹ค ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ๋ผ๋„ ๋ฆฌ๋ทฐ๋ฅผ ๋‚จ๊ฒจ๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. 

์ œ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ , ๊ธฐ๋Šฅ๊ณผ ์„ค๋ช…์„ ์ •๋ฆฌํ•ด๋‘” ๊นƒํ—ˆ๋ธŒ ๋งํฌ์ž…๋‹ˆ๋‹ค. (์™„์ „ํ•œ ์ „์ฒด์ฝ”๋“œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

https://github.com/deslog/TodoList

 

GitHub - deslog/TodoList

Contribute to deslog/TodoList development by creating an account on GitHub.

github.com


0. ์ค€๋น„

์šฐ๋ฆฌ๋Š” ์•ฑ๊ฐœ๋ฐœ์„ ํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, project๋ฅผ ์ƒ์„ฑํ•  ๋•Œ iOS์—์„œ App์„ ์„ ํƒํ•ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ํŒŒ์ผ๋ช…์€ TodoList๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ๊ณ , ์ฃผ ์–ธ์–ด๋Š” Swift๋ฅผ ์„ ํƒํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํŒŒ์ผ๋“ค(?)์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ main.storyboard ์—์„œ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๊ณ , ๋‹ค์–‘ํ•œ ๋ฒ„ํŠผ, ํ™”๋ฉด์— ๊ธฐ๋Šฅ์„ ViewController.swift ํŒŒ์ผ์—์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์ €๋Š” ์ธํ„ฐ๋„ท ๊ฐ•์˜๋ฅผ ๋ณด๊ณ  ์ง„ํ–‰ํ•œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, main.storyboard ์— ui๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์€ ์ƒ๋žตํ•˜๊ณ , ViewController.swiftํŒŒ์ผ์— ์ž‘์„ฑํ–ˆ๋˜ ๊ธฐ๋Šฅ ์œ„์ฃผ๋กœ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.


1. ์™„์„ฑ๋œ ํ™”๋ฉด

์™„์„ฑ๋œ ํ™”๋ฉด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. 

์ดˆ๊ธฐ ํ™”๋ฉด์˜ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์—๋Š” Edit๋ฒ„ํŠผ, ์™ผ์ชฝ ์ƒ๋‹จ์—๋Š” ํ•  ์ผ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” + ๋ฒ„ํŠผ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•  ์ผ ๋“ฑ๋ก (+)๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ํŒ์—…์ฐฝ์— ํ• ์ผ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” alert์ฐฝ์ด ๋œจ๊ณ , ๊ทธ ์•ˆ์— textField๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. textField์— ํ• ์ผ์„ ์ž…๋ ฅํ•˜๊ณ  ๋“ฑ๋ก๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ํ• ์ผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์ง„์—๋Š” ๋‚˜์™€์žˆ์ง€ ์•Š์ง€๋งŒ, ํ• ์ผ์„ ํ™”๋ฉด์—์„œ ํด๋ฆญํ•˜๋ฉด ์˜ค๋ฅธ์ชฝ์— V ํ‘œ์‹œ๊ฐ€ ๋˜๋ฉฐ ํ• ์ผ์ด ์™„๋ฃŒ๋๋‹ค๋Š” ํ‘œ์‹œ๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

edit๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ•  ์ผ cell ๋งˆ๋‹ค ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„ํŠผ (-)์ด ์™ผ์ชฝ์— ์ƒ์„ฑ๋˜๊ณ , ์˜ค๋ฅธ์ชฝ์—๋Š” ํ• ์ผ์˜ ์ˆœ์„œ๋ฅผ ๋ฐ”๊ฟ€์ˆ˜ ์žˆ๋Š” ์žฌ์ •๋ ฌ ๋ฒ„ํŠผ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ edit๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด์ง€ ์•Š๊ณ , ์™ผ์ชฝ์œผ๋กœ ์Šค์™€์ดํ”„ํ•ด์„œ ์‚ญ์ œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ๋” ์„ค์ •์„ ํ•ด๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€, ๋‹ค์‹œ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ฃ .


2. ViewController.swift ์ฝ”๋“œ ์‚ดํŽด๋ณด๊ธฐ (๊ธฐ๋Šฅ ๊ตฌํ˜„)

2.1 ํ•  ์ผ์„ ๋“ฑ๋กํ•˜๋ฉด ํ•  ์ผ๋“ค์ด ํ…Œ์ด๋ธ”๋ทฐ์— ํ‘œ์‹œ๋˜๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ

ํ˜„์žฌ ui๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด๋†“์€ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ table View์— ํ• ์ผ๋“ค์˜ ๋ชฉ๋ก์ด ํ‘œํ˜„๋˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” table View์— ํ• ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•œ edit๋ฒ„ํŠผ๊ณผ add(+) ๋ฒ„ํŠผ์˜ action๊ธฐ๋Šฅ๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

class ViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet var editButton: UIBarButtonItem!
    var doneButton: UIBarButtonItem?
    var tasks = [Task]()  {
        didSet { // ํ”„๋กœํผํ‹ฐ ์˜ต์ €๋ฒ„, tasks ๋ฐฐ์—ด์— ํ• ์ผ์ด ์ถ”๊ฐ€๋  ๋•Œ๋งˆ๋‹ค ์œ ์ € ๋””ํดํŠธ์— ํ• ์ผ์ด ์ €์žฅ๋จ
            self.saveTasks()
        }
    }// Task ๋ฐฐ์—ด ์ƒ์„ฑ

์šฐ์„  viewController class์— tableView๋ผ๋Š” ์ด๋ฆ„์˜ IBOutlet ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๊ณ , editButton IBOulet๋ณ€์ˆ˜๋„ ์ƒ์„ฑํ•ด ์ค์‹œ๋‹ค. editButton์€ UIBarButtonItem ํ˜•ํƒœ๋กœ ํ˜•์„ฑํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. (doneButtonn, tasks ๋“ฑ์€ ์ถ”ํ›„ ๋˜ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ• ๋•Œ ๋ฆฌ๋ทฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์šฐ์„  ์•„์›ƒ๋ ›๋ณ€์ˆ˜๋งŒ ์ง‘์ค‘)

๊ทธ๋ฆฌ๊ณ  edit๊ณผ add ๋ฒ„ํŠผ์— ๋Œ€ํ•œ ์•ก์…˜ํ•จ์ˆ˜๋„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค. (@IBActionํ•จ์ˆ˜)

 @IBAction func tapeditButton(_ sender: UIBarButtonItem) {
        guard !self.tasks.isEmpty else { return }
        self.navigationItem.leftBarButtonItem = self.doneButton
        self.tableView.setEditing(true, animated: true)
    }
    
    
    // alert; ์ œ๋ชฉ, ๋ฉ”์„ธ์ง€, ํ•˜๋‚˜์ด์ƒ์˜ ๋ฒ„ํŠผ ๋ฐ ์ž…๋ ฅ์„ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ํ…์ŠคํŠธ ํ•„๋“œ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.
    @IBAction func tapAddButton(_ sender: UIBarButtonItem) {
        let alert = UIAlertController(title: "ํ•  ์ผ ๋“ฑ๋ก", message: "ํ•  ์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", 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)
            //๋“ฑ๋ก๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฟ์„๋•Œ ํ…์ŠคํŠธํ•„๋“œ์— ์žˆ๋Š” ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
            // textFields๋Š” ๋ฐฐ์—ด์ธ๋ฐ, ์šฐ๋ฆฌ๋Š” ํ•˜๋‚˜๋งŒ ๋„ฃ์–ด๋†ง๊ธฐ ๋•Œ๋ฌธ์— [0]๋กœ ์ ‘๊ทผํ–ˆ์Œ.
            self?.tableView.reloadData() // add๋œ ํ• ์ผ๋“ค์„ ํ…Œ์ด๋ธ”๋ทฐ์— ์ƒˆ๋กœ์ƒˆ๋กœ ์—…๋กœ๋“œํ•ด์ฃผ๋Š” ๊ฒƒ
        })
        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)
    
    }

 

โ–ถ tapAddButton ๋จผ์ € ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์•ž์—์„œ ์„ค๋ช…ํ–ˆ๋“ฏ์ด Add ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด alert์ฐฝ์„ ๋„์›Œ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ  alert ์ƒ์ˆ˜๋ฅผ ์„ ์–ธํ•ด์„œ UIAlertController๋ฅผ ์ด์šฉํ•ด์„œ alert์ด ํ‘œ์‹œ๋˜๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. title์€ "ํ• ์ผ ๋“ฑ๋ก", message๋Š” "ํ• ์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”", preferredStyle ์—๋Š” .alert์„ ์„ค์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

preferredStyle ์—๋Š” alert์™€ action์‹œํŠธ ๋‘๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ๋‘๊ฐ€์ง€์˜ ์ฐจ์ด๋Š” ๋‹ค์Œ๊ณผ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ทธ ๋‹ค์Œ registerbutton์ด๋ผ๋Š” ์ƒ์ˆ˜๋ฅผ ์„ ์–ธํ•œํ›„, UIAlertAction ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ alert์— ๋ฒ„ํŠผ์ด ์ถ”๊ฐ€๋˜๊ฒŒ ๋งŒ๋“ค์–ด์คฌ์Šต๋‹ˆ๋‹ค. Title์€ ๋“ฑ๋ก, style์€ ๋””ํดํŠธ๋กœ, handler ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ํด๋กœ์ €๋ฅผ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์•Œ๋Ÿฟ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„๋•Œ, ์‹คํ–‰ํ•ด์•ผํ•˜๋Š” ํ–‰๋™์„ ํด๋กœ์ €์•ˆ์— ์ •์˜ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋“ฑ๋ก๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ๋•Œ๋งˆ๋‹ค ํ• ์ผ์„ ์ถ”๊ฐ€ํ•ด์ค˜์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๊ธฐ๋Šฅ์„ ๋‹ด์€ ํด๋กœ์ €๋ฅผ ์ •์˜ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ทธ ์ดํ›„ alert ์ฐฝ์—๋Š” cancle ๋ฒ„ํŠผ๋„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ canclebutton ์„ ์„ ์–ธํ•ด์ฃผ์—ˆ๊ณ , style์€ cancle, ํ–‰๋™ํ•ด์•ผํ•  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ •์˜ํ•˜๋Š” handler์—๋Š” nil๋กœ ์ •์˜ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  alert.addAction ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฐฉ๊ธˆ ๋งŒ๋“  uibutton์„ ๋„ฃ์–ด์ฃผ๋ฉด๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์šฐ๋ฆฌ๋Š” alert์ฐฝ์—์„œ ํ• ์ผ์— ๋Œ€ํ•œ ์ž…๋ ฅ์„ ๋ฐ›์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, textField๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. textField๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ alert.addTextFields ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. configurationhandler ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ํด๋กœ์ €๋ฅผ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ, ์•Œ๋Ÿฟ์„ ํ‘œ์‹œํ•˜๊ธฐ ์ „์— ํ…์ŠคํŠธํ•„๋“œ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ํด๋กœ์ €์ž…๋‹ˆ๋‹ค. ์ด ํด๋กœ์ €๋Š” ๋ฐ˜ํ™˜๊ฐ’์ด ์—†๊ณ , textField ๊ฐ์ฒด์— ํ•ด๋‹นํ•˜๋Š” ๋‹จ์ผ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•œ๋งˆ๋””๋กœ ์•Œ๋Ÿฟ์— ์‚ฌ์šฉํ•˜๋Š” ํ…์ŠคํŠธํ•„๋“œ๋ฅผ ์„ค์ •ํ•˜๋Š” ํด๋กœ์ €๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” , "ํ• ์ผ์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" ๋ผ๊ณ  placeholder๋กœ ํ‘œ์‹œ๋˜๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ด์ œ ์ด๋ ‡๊ฒŒ ์•ก์…˜์„ ์„ค์ •์™„๋ฃŒํ–ˆ์œผ๋‹ˆ,

โ–ท ์‚ฌ์šฉ์ž๊ฐ€ ์ง„์งœ ์ž…๋ ฅํ•œ ํ• ์ผ์„ tasks ๋ผ๋Š” ๋ฐฐ์—ด์— ์ €์žฅํ•ด์ค์‹œ๋‹ค. (ํ…Œ์ด๋ธ”๋ทฐ์— ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ์ €์žฅ์ด ํ•„์š”)

task๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. New File์„ ๋งŒ๋“ค์–ด์„œ, task.swfitํŒŒ์ผ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์•ˆ์— task๋ฅผ ์ •์˜ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

//
//  Tesk.swift
//  TodoList
//

import Foundation

struct Task {
    var title: String // ํ• ์ผ ๋‚ด์šฉ ์ €์žฅ
    var done: Bool // ํ• ์ผ์ด ์™„๋ฃŒ ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€ ์ €์žฅ
}

title ํ”„๋กœํผํ‹ฐ์—๋Š” ํ• ์ผ ๋‚ด์šฉ์„ ์ •์˜ํ•ด์ค„๊ฒƒ์ด๊ณ , done์ด๋ผ๋Š” ํ”„๋กœํผํ‹ฐ์—๋Š” ์™„๋ฃŒ ๋˜์—ˆ๋Š”์ง€์— ๋Œ€ํ•œ ์—ฌ๋ถ€๋ฅผ ์ •์˜ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด์ œ ํ• ์ผ๋“ค์„ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด์ค์‹œ๋‹ค.

viewController ํŒŒ์ผ๋กœ ๋Œ์•„๊ฐ€์„œ, class์—  tasks๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์•ž์„œ registerButton handler ํด๋กœ์ € ๋ถ€๋ถ„์— tasks ๋ฐฐ์—ด์— ํ• ์ผ์ด ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ๋„๋ก ์บก์ณ๋ชฉ๋ก์„ ์ •์˜ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ํด๋กœ์ € ์„ ์–ธ๋ถ€๋ถ„์—์„œ ์บก์ณ๋ชฉ๋ก์„ ์ •์˜ํ•ด์ฃผ๋Š” ์ด์œ ๋Š”, ํด๋ž˜์Šค์ฒ˜๋Ÿผ ํด๋กœ์ €๋Š” ์ฐธ์กฐํƒ€์ž…์ด๊ธฐ ๋•Œ๋ฌธ์— ํด๋กœ์ €์˜ ๋ณธ๋ฌธ์—์„œ self๋กœ  ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์บก์ณํ• ๋•Œ ๊ฐ•ํ•œ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ•ํ•œ ์ˆœํ™˜์ฐธ์กฐ๋ž€, ๋‘๊ฐœ์˜ ๊ฐ์ฒด๊ฐ€ ์ƒํ˜ธ ์ฐธ์กฐํ•˜๋Š” ๊ฒฝ์šฐ ๊ฐ•ํ•œ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์ˆœํ™˜์ฐธ์กฐ์™€ ์—ฐ๊ด€๋œ ๊ฐ์ฒด๋“ค์€ ๋ ˆํผ๋Ÿฐ์Šค ์นด์šดํŠธ๊ฐ€ 0์— ๋„๋‹ฌํ•˜์ง€ ์•Š๊ฒŒ ๋˜๊ณ , ๊ฒฐ๊ตญ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๊ฐ•ํ•œ ์ˆœํ™˜์ฐธ์กฐ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํด๋กœ์ €์˜ ์ˆœํ™˜๋ถ€์—์„œ ์บก์ณ๋ชฉ๋ก์„ ์ •์˜ํ•จ์œผ๋กœ์จ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” [weak  self]๋ฅผ ์ž‘์„ฑํ•˜๋ฏ€๋กœ์จ ์บก์ณ๋ชฉ๋ก์„ ์ •์˜ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. (์ดํ›„ ๋˜ ๋‹ค๋ฃฐ ์˜ˆ์ •์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค . ๊ทธ๋•Œ ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค ใ… ใ…  ) ํด๋กœ์ €์˜ ์„ ์–ธ๋ถ€์— weak๋‚˜ unknown ํ‚ค์›Œ๋“œ๋กœ ์บก์ณ๋ชฉ๋ก์„ ์ •์˜ํ•˜์ง€ ์•Š๊ณ , ์ ‘๊ทผํ•˜๊ฒŒ ๋˜๋ฉด ๊ฐ•ํ•œ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜์–ด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ๋งŒ ์•Œ์•„๋‘์ž.

๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€์„œ,

textfield๋Š” ์˜ต์…”๋„๋กœ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— guard๋กœ ๋ฐ”์ธ๋”ฉ์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  tasks ์— ๋ฐฉ๊ธˆ ๋งŒ๋“ค์–ด์ค€ task ์ธ์Šคํ„ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด์„œ ์•Œ๋Ÿฟ์ฐฝ์— ํ• ์ผ์„ ๋“ฑ๋กํ• ๋–„๋งˆ๋‹ค tasks์— ํ• ์ผ๋“ค์ด ์ถ”๊ฐ€๋˜๋„๋ก ๋งŒ๋“ค์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

โ–ท ๋‹ค์Œ์œผ๋กœ tasks ๋ฐฐ์—ด์— ์ €์žฅ๋˜์–ด์žˆ๋Š” ํ• ์ผ๋“ค์„ tableView์— ๋‚˜ํƒ€๋‚ด์–ด๋ด…์‹œ๋‹ค.

uI table View ๋ฐ์ดํ„ฐ์†Œ์Šค ํ”„๋กœํ† ์ฝœ์„ viewController์— ์ฑ„ํƒํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. 

    override func viewDidLoad() {
        super.viewDidLoad()
        self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTap))
        // Do any additional setup after loading the view.
        self.tableView.dataSource = self
        self.tableView.delegate = self
        self.loadTasks() // ์œ ์ €๋””ํด์ธ ์— ์ €์žฅ๋œ ํ• ์ผ์„ ์•ฑ์„ ๊ป๋‹ค ์ผœ๋„ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์™€์ฃผ๋Š”๊ฒƒ
        
    }

์ด๋ ‡๊ฒŒ viewDidLoad ํ•จ์ˆ˜์— dataSource์™€ delegate๋ฅผ self๋กœ ์ฑ„ํƒํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๊ณ  ๋‚˜๋ฉด, UITableViewDataSource๋ฅผ ์ฑ„ํƒํ•˜๋ผ๋Š” ๊ฒฝ๊ณ ๊ฐ€ ๋œจ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ class ์™ธ๋ถ€์— extension์„ ์ด์šฉํ•ด์„œ 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)
        // ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‚ญ๋น„ํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ dequeueResusableCell์„ ์ด์šฉํ•ด์„œ ์…€์„ ์žฌ์‚ฌ์šฉ ํ•˜๋Š” ๊ฒƒ
        let task = self.tasks[indexPath.row]
        cell.textLabel?.text = task.title
        
        // ์…€ ํ‘œ์‹œ๋์„ ๋•Œ ์ฒดํฌ๋งˆํฌ ํ‘œ์‹œ๋˜๊ฒŒ ํ•˜๋Š” ์ฝ”๋“œ
        if task.done {
            cell.accessoryType = .checkmark
        } else {
            cell.accessoryType = .none
        }
        return cell
    }

cell.textLabel?.text = task.title ์ฝ”๋“œ ๋ถ€๋ถ„์—์„œ task์— ์ž…๋ ฅ๋œ title๋“ค์„ cell์— ๋‹ด์•„์ฃผ๊ณ , return cell์„ ํ†ตํ•ด ๋ฐ˜ํ™˜ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ• ์ผ์„ ๋“ฑ๋กํ•  ๋•Œ๋งˆ๋‹ค ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ๊ฐฑ์‹ ํ•˜์—ฌ ๋ณด์—ฌ์ฃผ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— tapAddButton ํ•จ์ˆ˜๋กœ ๋‹ค์‹œ ๋Œ์•„๊ฐ€์„œ, self?.tableView.reloadData() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ…Œ์ด๋ธ”๋ทฐ์— ์žˆ๋Š” ๋‚ด์šฉ๋“ค์„ reload ํ•ด์ค„ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์™„๋ฃŒํ•˜๋ฉด, add ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค, ํ…Œ์ด๋ธ”๋ทฐ์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋“œ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ์™„๋ฃŒํ•œ๋‹ค๋ฉด, ๋ฌธ์ œ์ ์ด ์žˆ๋Š”๋ฐ ์•ฑ์„ ์ข…๋ฃŒํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š๊ณ  ์ž…๋ ฅํ–ˆ๋˜ ํ• ์ผ๋“ค์ด ๋ชจ๋‘ ์‚ญ์ œ๋œ๋‹ค๋Š” ๊ฒƒ์ธ๋ฐ, ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” userdefaults๋ฅผ ์ด์šฉํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๋“ค์ด ์•ฑ์„ ์žฌ์‹คํ–‰ํ•˜๋”๋ผ๋„ ๋ชจ๋‘ ์‚ด์•„์žˆ๋„๋ก ๋”ฐ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ธ€ ์—์„œ๋Š” userDefaults๋ฅผ ์ด์šฉํ•œ ์ฝ”๋“œ ๋ฆฌ๋ทฐ์— ๋Œ€ํ•ด์„œ ์—…๋กœ๋“œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์„ค๋ช…ํ•˜๊ณ , ์ œ๊ฐ€ ์•Œ์•„๋“ฃ๊ฒŒ ๋ฌธ์„œํ™”ํ•˜๋Š๋ผ ์ฝ”๋“œ๊ฐ€ ๋’ค์ฃฝ๋ฐ•์ฃฝ์ธ๋ฐ, github.com/deslog/TodoList์— ๋ฐฉ๋ฌธํ•˜์‹œ๋ฉด ์ „์ฒด์ฝ”๋“œ๋ฅผ ๋ณด์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•