シンプルに生きたいプログラマー日記

シンプルに生きたいプログラマのブログ

【レビュー】予定通り進まないプロジェクトの進め方

予定通り進まないプロジェクトの進め方

読んだ理由

  1. 自己目標達成のために役に立てられないか?
  2. 仕事に活かせられられないか?(実際の自分の仕事であるプロジェクトに活かせないか)

まとめ

・プロジェクト譜を使うことで、阻害要因の早期発見、対策に活かせそう
→ ただし、プロジェクト譜を書くのが手間
  書き方に慣れるのに時間がかかりそう
・プロジェクト譜を貯めて、将棋の感想戦のように振り返りをすれば、どのプロジェクトに活かせる応用力が身につきそう
→ 仕事に取り入れるのは難しそう。
  自分がプロジェクトマネージャーではないので今の進め方を変えることはできない。
  プロジェクト譜を貯めるためには、会社全体に理解してもらって少しづつ貯める必要がある。
  ただし、、個人的にプロジェクト譜をためて感想戦をすることで自分のために活かすことは出来そう。

プロジェクト譜とは?

プロジェクトのための編集ツール
・プロジェクトの変化や関係性を可視化することで、「問題」を理解、解決しやすくする
→ プロジェクト譜は、1枚で終わりではない。転換期に応じて、毎回書き直す。
 そのため、どういった経緯でプロジェクトの現在状態になったのかが、分かる。

 1週間単位で書くのが良いのか、良いのか1ヶ月単位で書けば良いのか、自分が状況が大きく変わった!というタイミングで
 新しく書き直すのか、そこに関しては本に書かれていなかった。多分、書きながら慣れていく必要があるのだと思われる
・プロジェクトマネージャーとしての力量を上げるための仮想演習を行える
→ プロジェクト譜を見ながら、自分だったらどうするか?を考え、プロジェクトマネージャーとして仮想訓練する

1) 勝利条件(獲得条件)→ プロジェクトの最終目標
2) 中間目的 → 勝利条件を達するための条件の1つ
3) 施策 → 中間目標を達成するために取るべき具体的な行動
4) 廟算八要素 → 「メンバー/人材」「予算」「納期」「機能」「ビジネスモデル」「環境」「競合」「外敵」。現在分かるべき情報
5) 事象 → 施策を行なった結果、どうなったか?ありのままの状況を記載。

自分で書いてみたプロジェクト譜(TOEICで800点を取るためには?)

f:id:nananiku-s-60:20190203203811p:plain

1枚目です。
2枚目は書いてないですが、試験の対策方法を得て、
TOEICの勉強本を買って勉強する」よりも、公式が出している問題集をひたすら
解けば良いと知ることができましたので、2枚目は「問題集を解く」に変更しようと思います。

こんな感じで、状況が変わるたびに書く、、であっているのか。
やり方がいまいちですけど、続けていけばだんだん慣れていくと思います。

確かに、仕事のプロジェクトをこのように回してプロジェクト譜を貯めることができれば
新人プロジェクトマネージャーも力をつけやすいのかも。。。と感じた1冊でした。

実際にワークショップあれば参加してみたいと思いました

 

【レビュー】すべての知識を「20字」にまとめる 紙1枚! 独学法

すべての知識を「20字」にまとめる 紙1枚! 独学法

読んだ経緯

  1. 独学での学習効果を上げる方法を知りたかった

  2. 要点をまとめる術を得られれば、わかりやすく相手に伝えることに繋がると思ったから

まとめ

制約が、受動的知識から能動的知識へと変換する助けとなり学習効率をあげる。

・「20文字」と、学習目的への回答という制約を設けることで記憶に残りやすくなる
・制約に縛られた中で考えをまとめることで、与えられた知識から自分の知識へと変換される
・Why、What(学習の目的)、How(どうやって学習内容を活かすか)を意識することで、知識を定着させやすくする

3Qへの回答を、要約し動作レベルで伝えると伝わりやすい

・Why、What(学習の目的)、How(どうやって学習内容を活かすか)で、質問と回答を分けることで伝えたいことがはっきりする
・抽象的な回答ではなく、具体的な行動レベルで相手に伝えることで伝わえやすくなる
(例:お金を稼ぐ → コンビニでアルバイトをして月給800円稼ぐ)

感想

著者が提案しているツールは使いこなすまでに時間がかかりそうだし、あまり自分には向いていないと感じた。
あくまで著者が試行錯誤を重ねて自分にあったツールであり、万人に通じるツールではないのかと思う。

ただ、それを以上にためになったのが「制約」と、「目的をはっきりさせて学習に取り組む」という考え。

ツール自体は、自分自身で試行錯誤するのがベストだと感じたが、
学習目的をはっきりさせる、制約を設ける、自分自身で考える、この3点を常に意識することで独学力を上げれるという 確信が得られた良い本だった。

20文字でまとめるのは、、、まだまだ難しい。。

gradleのバージョンを上げたことで、apkファイルの出力先が変わった

【現象】

・gradleファイルのバージョンを一律バージョンアップ
・gradleファイルに記載している、デプロイゲートへアップデートする参照先が見当たらずエラー
 > apkフォルダ配下にapkを指定していた
・apkファイルは、apk/release配下に置かれていた。。

【原因】

gradleのバージョンを2系から3系にバージョンアップしたのが原因。
バージョンアップしたことによって、apkファイルの置き場所が変わった模様

com.android.tools.build.gradle:2

com.android.tools.build.gradle:3

【解決方法】

・gradleファイルに記載している、デプロイゲートへアップデートする参照先を変更

【参考URL】

gradle-plugin 3.0 apk output location changed to build/outputs/apk//release/

ローカルPUSH通知

ローカルPUSH通知

使う機会があったのでまとめ

最低限の実装

1) PUSH通知許可依頼をユーザーに出す
2) フォアグランドでPUSH通知を受け取れるよう設定する
3) 通知内容を作成する
4) 通知を送る

ソースはGitに https://github.com/satoNobu/iOS_localPush/commits/master

1) PUSH通知許可依頼をユーザーに出す

自分の場合、ボタンを1個設定して、ボタン押下時に即PUSHが通知されるよう設定した

UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { (granted, error) in 
  // 許可結果応じた内容
    // 私がGitにあげてるソースでは、アラートで許可状態がわかるよう記述してある
}

UNUserNotificationCenter

通知関連のアクティビティを管理するための中心的なオブジェクト

current()

通知センターオブジェクトを返却

requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { (granted, error) in

ローカル、リモート通知許可をユーザーに要求する
options:アプリが要求している認証オプション。
この場合、バッチ、サウンド、アラート表示の承認を要求

completionHandler:承認結果を受け取るところ。
この場合、grantedに許可結果がbooleanで返ってくる

2) フォアグランドでPUSH通知を受け取れるよう設定する

// デリゲートメソッドを使えるよう設定
UNUserNotificationCenter.current().delegate = self
// UNUserNotificationCenterDelegateを継承して処理記述
extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
            completionHandler([ .badge, .sound, .alert ])
    }
}
UNUserNotificationCenterDelegate

通知関連のデリゲートメソッドインターフェイス

func userNotificationCenter(_ center: UNUserNotificationCenter,willPresent notification: UNNotification,withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)

アプリがフォアグラウンドで実行されている間に到着した通知を処理する方法をデリゲートに求める

3) 通知内容を作成する

let content = UNMutableNotificationContent()
content.title = "PUSH1"
content.body = "ボタンタップでPUSH通知"
content.sound = UNNotificationSound.default
UNMutableNotificationContent

通知コンテンツを作成
ここでは、タイトル、内容、通知音(デフォルト音)を設定。

4) 通知を送る

let request = UNNotificationRequest(identifier: "immediately", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
UNNotificationRequest

ローカル通知の配信をスケジュールするオブジェクトを作成

identifier:通知要求の識別子
content:通知内容
trigger:通知の配信をトリガーする条件。

--- ここまでの実装でボタンタップしてローカルPUSHが送れるとこまで完成

イメージ以下

f:id:nananiku-s-60:20190122225012p:plain

「PUSH1」のボタンをタップすると、フォアグランド上でローカルPUSH通知される

「PUSH2」のボタンの処理は、下の方に記載ある 5)で記載してあるトリガーを使ったもの。
「PUSH2」のボタンをタップすると、5秒後にローカルPUSHが通知される。
これは、ボタンをタップ後アプリをバックグランドにしたり、タスクキルしても通知される
(なので、2)の設定をコメントアウトしてもこの処理は動く。ただし、フォアグランドでは動かなくなるが。。。)

 --- 以下、諸々追加

時間あれば、どんどん追記していきます。
今の所考えてる内容
・通知領域タップ時の処理
・バッジ
・通知領域に画像
・位置情報を元にローカルPUSH
・アクション付きの通知領域
・通知許可を再度出す

5) トリガーを設定して時間経過でPUSHがくるようにする

// トリガーを設定。5秒後に通知がくる
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
// UNNotificationRequestに設定したトリガー指定する
let request = UNNotificationRequest(identifier: "localPushTest2", content: content, trigger: trigger)
UNTimeIntervalNotificationTrigger

指定された時間が経過した後に通知が配信されるようにするトリガー
timeInterval:時間指定
repeats:リピートするかどうか。trueの場合、60秒以上である必要がある

参考URL

ものすごくわかりやすかったです
猿でも分かるプッシュ通知 iOS 10以降のNotificationの基本 iOS 10 User Notifications Framework実装まとめ

UITabBarControllerの使い方

成果物

・タブで画面切り替え
・タブにバッチがつける。タブを押すたびにバッチに+1される
f:id:nananiku-s-60:20190122224521p:plain

作成手順

・storyboard上で初期のViewControllerからtabBarControllerに差し替える
(矢印とControllerも差し替え)

f:id:nananiku-s-60:20190122224545p:plain

・最初は、タブが2つ。もう1つ付け足す。
1)ViewControllerを追加
2)TabBarControllerを"Control"キーを押しながら、新規追加したViewControllerまで引っ張る
3)Relationship Segue のview controllersを選択
f:id:nananiku-s-60:20190122224608p:plain

4) Relation shipが増えてることを確認
f:id:nananiku-s-60:20190122224623p:plain

・まず、各画面に対応したControllerクラスを作成する。
以下、1つ目。あとは、同様に作成。切り替え時にわかりやすいように背景色とラベル名だけ変える

import UIKit

class View3: UIViewController {
    
    var pageLabel : UILabel?
    override func viewDidLoad() {
        super.viewDidLoad()
        pageLabel = UILabel()
        pageLabel!.frame = CGRect(x:20, y:80, width:self.view.bounds.size.width, height:20)
        pageLabel!.backgroundColor = UIColor.clear
        pageLabel!.text = "3"
        self.view.addSubview(pageLabel!)
        self.view.backgroundColor = UIColor.green
    }
}

・storyboard上で、作成したクラスとの関連付けも忘れないようにする!
f:id:nananiku-s-60:20190122224646p:plain

・継承クラスをUITabBarControllerに変更
・タブのタイトルとタグを設定。タグはタブを押下された時の出しわけで必要

// タブのタイトルとタグを設定 con1.tabBarItem.title = "AAAAA" con1.tabBarItem.tag = 1

タブの配置順と、それを反映させる処理。

let conList : Array = [con1, con2, con3] self.setViewControllers(conList, animated: false)

タブが押下された時の処理をこの中に記述する
*選択された画面を一番上に表示する。画面のリロードなどは行わないので、行いたい場合は別で処理を記述する必要あり

tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {}

・タブにバッチを付ける部分

con1.tabBarItem.badgeValue = String(con1Cnt)

import UIKit

class ViewController: UITabBarController {

    let con1 = View1()
    let con2 = View2()
    let con3 = View3()
    var con1Cnt : Int = 0
    var con2Cnt : Int = 0
    var con3Cnt : Int = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // タブのタイトルとタグを設定
        con1.tabBarItem.title = "AAAAA"
        con1.tabBarItem.tag = 1
        
        con2.tabBarItem.title = "BBBBB"
        con2.tabBarItem.tag = 2
        
        con3.tabBarItem.title = "CCCCC"
        con3.tabBarItem.tag = 3
        
        let conList : Array<UIViewController> = [con1, con2, con3]
        self.setViewControllers(conList, animated: false)
    }
    
    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        switch item.tag {
        case 1:
            con1Cnt += 1
            // バッチを指定
            con1.tabBarItem.badgeValue = String(con1Cnt)
        case 2:
            con2Cnt += 1
            // バッチを指定
            con2.tabBarItem.badgeValue = String(con2Cnt)
        case 3:
            con3Cnt += 1
            // バッチを指定
            con3.tabBarItem.badgeValue = String(con3Cnt)
        default:
            break
        }
    }
}

サンプル

https://github.com/satoNobu/study_swift/commit/edfbcd385711460c096da82a8cfba91ab3f28741

CollectionViewの使い方

出来るもの

1-100の数字をCollectionViewに表示する
数字をタップすると数字に応じたアラートが出る
横にスクロール出来る

f:id:nananiku-s-60:20190122224104p:plain

作成手順

・まずstoryboardに配置しているViewControllerをCollectionViewControllerに変更する
(もともとのViewControllerについてる矢印もこっちに付け替える。矢印は初期画面を指すので)
・cellのIdentifierも設定する。安直に"Cell"って名前を設定。
f:id:nananiku-s-60:20190122224122p:plain

・cell内に数字を表示するためのラベルを設定
・tagも設定する。"1"で設定。

f:id:nananiku-s-60:20190122224141p:plain

・ViewControllerが継承しているクラスをUICollectionViewControllerに変更
・コレクションに表示するデータ1-100を配列で用意
・セルの詳細なレイアウトを設定する
    ・セルのサイズ
    ・縦・横のスペースをどれくらいとるか
    ・スクロールを縦にするのか、横にするのか
    ・・など

class ViewController: UICollectionViewController {

    // コレクションに表示するデータ
    var items = [String]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // セルの詳細なレイアウトを設定する
        let flowLayout : UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        // セルのサイズ
        flowLayout.itemSize = CGSize(width: 100.0, height: 100.0)
        // 縦・横のスペース
        flowLayout.minimumLineSpacing = 10.0
        flowLayout.minimumInteritemSpacing = 12.0
        //  スクロールの方向
        flowLayout.scrollDirection = UICollectionView.ScrollDirection.horizontal
        // 上で設定した内容を反映させる
        self.collectionView.collectionViewLayout = flowLayout
        // 背景色を設定
        self.collectionView?.backgroundColor =  UIColor.lightGray
        
        // 配列を初期化(コレクションに表示するデータを作成)
        for i in 0 ..< 100 {
            items.append(String(i))
        }
    }

・セクション(データをグルーピングしたもの)の数を決める。今回はセクションを分けるつもりはないので1固定

    // セクションの数を決める
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

・セクション内のセルの数を決める。セルの数=データの数。なので、1-100のデータを詰めた配列の数を返す

   // セクション内のセルの数を決める
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

・セルのデータを表示する
・ここが一番重要。
まず、これ。stroyboardのcellに設定したIdentifierをここで取得。ソース内で関連付ける

collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)

・次にこれ。stroyboardのラベルに設定したタグを取得。ソース内で関連付ける

let label = cell.contentView.viewWithTag(1) as! UILabel

・IndexPath.rowに何番目の値かがわかるで、これをキーに配列からデータを取得

label.text = items[indexPath.row]

   // セルのデータを表示
    overrifde func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // セルを生成し、X番目に相当するデータを表示
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = UIColor.black
        
        let label = cell.contentView.viewWithTag(1) as! UILabel
        label.text =  items[indexPath.row]
        label.textColor = UIColor.red
        return cell
    }

・セルが選択された時の処理を指定する
・今回は、アラートにしているが、遷移でもOK
・注意することは、セルに表示されているデータそのものが渡されるわけでない点。
セルに相当するIndexPathの値が渡されるだけなので、表示の時と同じように.rowで配列からデータを取得する
・アラートの表示方法については割愛

   // セルが選択された時の処理を指定する
    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let alert : UIAlertController = UIAlertController(title: "タイトル", message: "\(items[indexPath.row])", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(okAction)
        present(alert, animated: true, completion: nil)
    }

Git

Gitにサンプルあげてます。

https://github.com/satoNobu/study_swift/commit/c25dad8cb51ef1c0ee44f996e7df278aea79e61c

TableViewの使い方

目的

TableViewで出来ることを模索する

→ TODOアプリなんかはTableViewを極めれば、ライブラリを使わずに実装できそう
追加、削除、データー配置移動まで出来る

反省点:セクションも入れたのでちょっと処理が面倒になった

作成手順

1.stroyboardにTableViewを追加(画面一杯まで)
2.TableViewCellを追加(1つだけでOK)
3.cellのIdentifier(識別子)を設定
4.NavigationControllerを追加(画面遷移用)
5.TableViewをView Controllerと関連付けする
6.セルのデータを指定する
7.セルのデータ数を指定する
------ここまでで、最低限の実装。ビルドできる
8.テーブルをセクションに分ける
9.テーブルが選択された時に画面遷移させる
10.テーブルの挿入・削除
11.テーブルのデータ移動

cellのIdentifier(識別子)設定方法

やり方は2通り。
A)のやり方の方が、楽。ただ、Bの方がソースからは追いやすい。


A) storyboard上で設定する
f:id:nananiku-s-60:20190122223118p:plain

B) ソースコード上で設定する
識別子を"cellTest"に設定する
識別子がない場合は、if文の中に入ってデフォルト値を設定する

// テーブルの行ごとのセルを返却する
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: "cellTest")
    if cell == nil {
        cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: cellIdentifier)
    }
    cell?.textLabel?.text = todoList[indexPath.row]
    return cell!
}

NavigationControllerの追加方法

Editor > Embed In > NavigationControllerで追加
f:id:nananiku-s-60:20190122223142p:plain

TableViewをView Controllerと関連付けする

1) 「Control」キーを押しながら、「TableView」を「View Controller」にドラッグ&ドロップ
2) Outletsの「dataSource」と「delegate」の両方を関連付ける
f:id:nananiku-s-60:20190122223157p:plain

3) ViewControllerにUITableViewDataSource, UITableViewDelegateプロトコルを宣言する

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

セルのデータ数を指定する

todoListは配列。
配列の数をretrunしてるだけ

// テーブルの行数を返却する
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return todoList.count
}

セルのデータ(表示内容)を指定する

indexPathが、テーブルの何番目かをさす

// テーブルの行ごとのセルを返却する
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
    if cell == nil {
        cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: cellIdentifier)
    }
    cell?.textLabel?.text = todoList[indexPath.row]
    return cell!
}

テーブルをセクションに分ける

1) セクション用の配列を用意。合わせて、セクション内に表示する配列も変更

var sectionList = ["section1","section2"]
var todoList = [["1","2"], ["1","2","3"]]

2) セクションの数を指定

// セクションの数を指定
func numberOfSections(in tableView: UITableView) -> Int {
    return sectionList.count
}

3) セクションのタイトルを設定

// セクションのタイトルを設定
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return sectionList[section]
}

4) テーブルの行数の返し方を変更
indexPathの中にセクションの情報もあるので設定する

// テーブルの行数を返却する
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return todoList[section].count
}
// テーブルの行ごとのセルを返却する
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
    if cell == nil {
        cell = UITableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: cellIdentifier)
    }
    // セルのタイトルの設定
    cell?.textLabel?.text = todoList[indexPath.section][indexPath.row]
    return cell!
}

テーブルが選択された時に画面遷移させる

1) 遷移先の画面を作成する
    ・storyboardにViewController追加
    ・Idetitiyを設定
f:id:nananiku-s-60:20190122223419p:plain

 ・新しくViewControllerクラス作成
 ラベルと、遷移元から渡させる文字列を設定する変数"text"を用意

import Foundation
import UIKit

class TableViewCellController: UIViewController {
    
    @IBOutlet weak var lable: UILabel!
    var text: String? 
    override func viewDidLoad() {
        super.viewDidLoad()
        lable.text = self.text
    }
}

3) セル選択時の処理を追加

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // ストリートボードから遷移先のクラス名を取得
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    // 1)で設定したIdetitiyを指定して取得
    let vc = storyboard.instantiateViewController(withIdentifier: "cellCon") as! TableViewCellController
    // タイトルとラベルに設定する文字列を指定
    vc.title = sectionList[indexPath.section]
    vc.text = todoList[indexPath.section][indexPath.row]
    // 遷移先のページを設定
    self.navigationController?.pushViewController(vc, animated: true)
}

テーブルの挿入・削除

1) Editボタンの設定

override func viewDidLoad() {
    super.viewDidLoad()
        
    // Editボタンを右上に配置
    self.navigationController?.isNavigationBarHidden = false
    navigationItem.title = "TableView"
    navigationItem.rightBarButtonItem = editButtonItem
}

2) テーブルのEditボタン押下時の設定

// テーブルの編集権限
override func setEditing(_ editing: Bool, animated: Bool) {
    // override前の処理を継続してさせる
    super.setEditing(editing, animated: animated)
    // tableViewの編集モードを切り替える
    if editing {
        let newIndexPath = IndexPath(row: todoList[1].count, section: 1)
        todoList[1].append("登録する")
        self.tableView.insertRows(at: [newIndexPath], with: UITableView.RowAnimation.fade)
    } else {
        let indexPath = IndexPath(row: todoList[1].count-1, section: 1)
        todoList[1].remove(at: indexPath.row)
        self.tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.fade)
    }
        tableView.isEditing = editing
    }

3) テーブルのセルの編集権限設定

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    // セクション0は編集させない
    if indexPath.section == 0 {
        return false
    }
    return true
}

4) テーブルの編集形式を設定

func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
    if indexPath.section == 1 && todoList[1].count == indexPath.row + 1 {
        return UITableViewCell.EditingStyle.insert
    }
    return UITableViewCell.EditingStyle.delete
}

5) テーブルのセルを挿入/削除する

// テーブルのセルを挿入/削除する
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if (editingStyle == UITableViewCell.EditingStyle.delete) {
        todoList[indexPath.section].remove(at: indexPath.row)
        self.tableView.deleteRows(at: [indexPath], with: UITableView.RowAnimation.fade)
    } else if (editingStyle == UITableViewCell.EditingStyle.insert) {
        todoList[indexPath.section].insert(String(indexPath.row+1), at: todoList[1].count-1)
        self.tableView.insertRows(at: [indexPath], with: UITableView.RowAnimation.fade)
    }
}

テーブルのデータ移動

1) テーブルのデータ移動権限設定

func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
    // セクション1の最初の1行目だけ移動を許可させない
    if indexPath.section == 1 && 0 == indexPath.row {
        return false
    }
    return true
}

2) テーブルのデータを移動する

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        // 特に処理は必要なし
}

用語

UITableViewDelegate

テーブルが操作された時にどんなアクションを行うか

UITableViewDataSource

テーブルの表示にどんなデータを使うか定義

参考URL

tableViewについて色々調べていて一番わかりやすかったサイト

https://pg-happy.jp/swift-tableview-tableviewcell.html

tableの編集について参考にしたサイト

https://qiita.com/nasutaro211/items/50d1dbc89969d873b7da

サンプル

https://github.com/satoNobu/study_swift/commit/69633e5fbc75da2c0234e171482210c959ca6abf