[iOSアプリ開発初期格差コース]Part 2.To Do Listアプリケーションの作成


To Do Listアプリケーション


Intro


[機能の説明]
→TableViewに保留を追加できる
→Table Viewでの保留を削除する
→Table Viewでは、やるべきことを再スケジュールできます
→やるべきことをデータベースに保存し、アプリケーションを再実行してもデータを保持できるようにする
[使用するテクノロジー]
→ UITableView
→ UIAlertController
→ UserDefaults

TableView


最も基本的なUIコンポーネントは、
  • データをリスト形式で表示することができる
  • 複数のセル、1列の複数行、
  • のみ垂直スクロール
  • の部分を使用して行をグループ化することにより、コンテンツ
  • をより容易にナビゲートすることができる.
  • 部のHeaderとFooterでビューを構成し、詳細
  • を表示する.
  • Delegate, DataSource
    →Table Viewを使用するために必要なプロトコル
    →UItableViewDelegate:Table Viewの表示部分の設定、行のアクション管理、ヘルパービューのサポート、およびTable Viewの各行の編集を支援する
    →UItableViewDataSource:TableViewオブジェクトの作成と変更に必要な情報
  • を提供します.

    UITableViewDelegate

    // 행이 선택되었을 때 호출되는 메소드
    optional func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
    
    // 행이 선택 해제되었을 때 호출되는 메소드
    optional func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath)
    
    // 특정 위치 행의 높이를 묻는 메소드
    optional func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    
    // 지정된 섹션의 헤더뷰 또는 풋터뷰에 표시할 View가 어떤 것인지 묻는 메소드
    optional func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
    optional func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
    
    // 지정된 섹션의 헤더뷰 또는 풋터뷰의 높이를 묻는 메소드
    optional func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
    optional func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
    
    // 테이블뷰가 편집 모드에 들어갔을 때 호출되는 메소드
    optional func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath)
    
    // 테이블뷰가 편집 모드에서 빠져나왔을 때 호출되는 메소드
    optional func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?)
    
    // 테이블뷰가 셀을 사용하여 행을 그리기 직전에 호출되는 메소드
    optional func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
    
    // 테이블뷰로부터 셀이 화면에서 사라지면 호출되는 메소드
    optional func tableView(_ tableView: UITableView, didEndDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)

    UITableViewDataSource

    // 각 섹션에 표시할 행의 개수를 묻는 메소드(필수)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    
    // 특정 인덱스 Row의 Cell에 대한 정보를 넣어 Cell을 반환하는 메소드(필수)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    
    // 총 섹션 개수를 묻는 메소드
    optional func numberOfSections(in tableView: UITableView) -> Int
    
    // 특정 섹션의 헤더 타이틀을 묻는 메소드
    optional func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
    
    // 특정 섹션의 풋터 타이틀을 묻는 메소드
    optional func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String?
    
    // 특정 위치의 행이 편집 가능한지 묻는 메소드
    optional func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
    
    // 특정 위치의 행을 재정렬할 수 있는지 묻는 메소드
    optional func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool
    
    // 테이블 뷰 섹션 인덱스 타이틀을 묻는 메소드
    optional func sectionIndexTitles(for tableView: UITableView) -> [String]?
    
    // 인덱스에 해당하는 섹션을 알려주는 메소드
    optional func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int
    
    // 스와이프 모드, 편집 모드에서 버튼을 선택하면 호출되는 메소드
    // 해당 메소드에서는 행의 변경 사항을 Commit해야 함
    optional func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
    
    // 행이 다른 위치로 이동되면 어디서 어디로 이동했는지 알려주는 메소드
    optional func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)

    きのうインプリメンテーション

    //
    //  ViewController.swift
    //  ToDoList
    //
    //  Created by TAEJANIM on 2021/11/09.
    //
    
    import UIKit
    
    class ViewController: UIViewController {
        
        @IBOutlet weak var tableView: UITableView!
        @IBOutlet var editButton: UIBarButtonItem!
        
        var doneButton: UIBarButtonItem?
        var tasks = [Task]() {
            didSet {
                self.saveTasks()
            }
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTap))
            self.tableView.dataSource = self
            self.tableView.delegate = self
            self.loadTasks()
        }
        
        @objc func doneButtonTap() {
            self.navigationItem.leftBarButtonItem = self.editButton
            self.tableView.setEditing(false, animated: true)
        }
    
        @IBAction func tapEditButton(_ sender: UIBarButtonItem) {
            guard !self.tasks.isEmpty else { return }
            self.navigationItem.leftBarButtonItem = self.doneButton
            self.tableView.setEditing(true, animated: true)
        }
        
        @IBAction func tapAddButton(_ sender: UIBarButtonItem) {
            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()
            })
            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)
        }
        
        func saveTasks() {
            let data = self.tasks.map {
                [
                    "title": $0.title,
                    "done": $0.done
                ]
            }
            let userDefaults = UserDefaults.standard
            userDefaults.set(data, forKey: "tasks")
        }
        
        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)
            }
        }
    }
    
    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) {
            self.tasks.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
            
            if self.tasks.isEmpty {
                self.doneButtonTap()
            }
        }
        
        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
        }
    }
    
    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
            self.tableView.reloadRows(at: [indexPath], with: .automatic)
        }
    }
    //
    //  Task.swift
    //  ToDoList
    //
    //  Created by TAEJANIM on 2021/11/09.
    //
    
    import Foundation
    
    struct Task {
        var title: String
        var done: Bool
    }

    Outro

  • UItable View:データをリスト形式で表示できる最も基本的なUIコンポーネント
    →1列と複数行で垂直スクロールのみ
    →UItableViewDataSource:データを受信してビューを描画する役割
    →UItableViewDelegate:
  • 、データを受信してテープビューを表示する
  • UIalertViewControl:Alertウィンドウを構成し、アプリケーションに表示する
  • ユーザーDefaults:ランタイム環境の操作インタフェース
  • 、アプリケーションのランタイムにプライマリ・リポジトリにアクセスしてデータを記録およびインポート

    結果