31623


プログラミング初心者です。StackOverflowを使うのも初めてなので、うまくかけていないところがありましたら申し訳ございません。

学校のプロジェクトでアプリを作ることになったのですが、初めてなのうまくいかず困っています。皆様の力を貸していただけたらなと思います。

私が使っているのは：  
Xcode Version 8.1  
Swift (Swiftのバージョンについてもよくわかっていないのですが、3.0だと思います）

私が作っているアプリは To Do List で、YouTubeのチュートリアルを見て作成しています。こちらが私の見ているチュートリアルです:  
<https://www.youtube.com/watch?v=Md0KDy6DCAc>

また、この方のフルコードが載っているサイトがこれです：  
<https://github.com/mobilespace/ToDoList>

私が今困っているのは、シミュレーターで機能がうまく動かないことです。 To Doアイテムを選択して左上のフォルダのマークを押すと、To Do Items
Completedに自動的に保存（名前がそちらに移動）される仕組みになるはずなのですが、私の場合、リストから名前が消えるだけで To Do Items
Completed に保存されません。

注意マークは一つも出てないので、何が間違っているのか、またどうやったら直るのかがわかりません。

よろしくお願いします。

ViewController.swift

[code]

    import UIKit
    
    class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddToDoItemControllerDelegate {
    
        @IBOutlet weak var tableView: UITableView!
    
        var toDoList: NSMutableArray = ["Go get groceries", "Walk the dog", "Watch a movie", "Do your homework"]
    
        var completedToDoList: [Int:String] = [:]
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            tableView.delegate = self
            tableView.dataSource = self
        }
    
        override func viewWillAppear(_ animated: Bool) {
            resetAccessoryType()
            tableView.reloadData()
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return toDoList.count 
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoCell", for: indexPath)
            cell.textLabel?.text = toDoList[indexPath.row] as? String
    
            return cell
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        func resetAccessoryType() {
            for row in 0..<toDoList.count {
                if let cell = tableView.cellForRow(at: IndexPath(row: row, section: 0)) {
                    cell.accessoryType = .none
                }
            }
        }
    
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            tableView.deselectRow(at: indexPath, animated: true)
    
            if let cell = tableView.cellForRow(at: indexPath) {
                if cell.accessoryType == .none {
                    cell.accessoryType = .checkmark
                    completedToDoList[completedToDoList.count] = toDoList[indexPath.row] as? String
                    toDoList.removeObject(at: indexPath.row)
                } else {
                    cell.accessoryType = .none
                }
            }
        }
    
        func addToDoItemToList(_ text:String) {
            toDoList[toDoList.count] = text
    
            tableView.reloadData()
        }
    
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if(segue.identifier == "AddToDoItemSegue") {
                let navigationController = segue.destination as! UINavigationController
                let addToDoItemViewController = navigationController.topViewController as! AddToDoItemController
    
                addToDoItemViewController.delegate = self
            } else if(segue.identifier == "CompletedToDoItemsSegue") {
                let completedToDoItemsController = segue.destination as! CompletedToDoItemsController
                completedToDoItemsController.completedToDoList = completedToDoList 
            }
        }
    
    }
    
[/code]

CompletedToDoItemController

[code]

    import UIKit
    
    class CompletedToDoItemsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
        @IBOutlet weak var tableView: UITableView!
    
        var completedToDoList: [Int:String] = [:]
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            tableView.delegate = self
            tableView.dataSource = self
            // Do any additional setup after loading the view.
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return completedToDoList.count 
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CompletedToDoItemCell", for: indexPath)
            cell.textLabel?.text = completedToDoList[indexPath.row]
    
            return cell
        }
    }
    
[/code]


CC BY-SA 3.0
2017-01-04T14:52:52.443
31621
2017-01-05T03:36:17.917
2017-01-05T02:03:02.193
20099
20099
question
swift xcode
シミュレーターで機能が思ったように動きません


まず完了済みToDOを表示するCompletedToDoItemsController.swiftをよく見ると、`CompletedToDoItemsController`の中ではほとんど何のデータ処理もしておらず(「保存」されたデータを読み込むような処理は一切存在しない)と言うのがわかります。

リンク先のGitHubにあるコードは(Swift2用なのでSwift3に変換すれば、ですが)期待通りの動作をしますから、どこかでデータを渡しているはずです。

メインのTableViewを表示しているViewController.swiftに戻ると、完了済みに関する動作をしているのはこの2箇所です。

TableView内の行(row)が選択されたときに呼び出されるこの処理:

[code]

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    
        if let cell = tableView.cellForRow(at: indexPath) {
            if cell.accessoryType == .none {
                cell.accessoryType = .checkmark
                completedToDoList[completedToDoList.count] = toDoList[indexPath.row] as? String
                toDoList.removeObject(at: indexPath.row)
            } else {
                cell.accessoryType = .none
            }
        }
    }
    
[/code]

と、セグエを使った画面遷移の際に呼び出されるこの処理:

[code]

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if(segue.identifier == "AddToDoItemSegue") {
            let navigationController = segue.destination as! UINavigationController
            let addToDoItemViewController = navigationController.topViewController as! AddToDoItemController
    
            addToDoItemViewController.delegate = self
        } else if(segue.identifier == "CompletedToDoItemsSegue") {
            let completedToDoItemsController = segue.destination as! CompletedToDoItemsController
            completedToDoItemsController.completedToDoList = completedToDoList 
        }
    }
    
[/code]

と言うことになります。(`completedToDoList`と言うわかりやすい名前をつけているので、比較的に関係ありそうな部分を絞り込むのが容易になっています。)

簡単に動作を説明すると、

  * TableView内の行にチェックマークをつけるときに`ViewController`内の`toDoList`からそのデータを削除し、`ViewController`内の`completedToDoList`にそのデータを追加する。

  * `"CompletedToDoItemsSegue"`と言うIDの付いたセグエで遷移するときに、`ViewController`内の`completedToDoList`の内容を`CompletedToDoItemsController`内の`completedToDoList`に丸ごとコピーする。

と言う動作になっています。

1つ目の処理で一緒に行っている「チェックマークをつける」「`ViewController`内の`toDoList`からそのデータを削除」までは正しく動いているのですから、その後の「`ViewController`内の`completedToDoList`にそのデータを追加」だけが動いていない可能性は低いでしょう。

つまりあなたのアプリがうまく動かない原因は、2個目の「セグエで遷移」の処理が正しく動いていないから、と言う可能性が高そうです。

* * *

この行:

[code]

    let completedToDoItemsController = segue.destination as! CompletedToDoItemsController
    
[/code]

の上にブレークポイントを設定(しょっちゅう使いますので、まだ覚えていなかったらすぐにでもやり方を覚えてください)して実行してみればわかるはずですが、おそらくフォルダアイコンのボタンをタップしてもこの部分で実行がブレークすることはないはずです。

と言うわけで上記の通りであれば、あとはstoryboardの設定を見直すことになります。画面遷移そのものはできているようなので、一番怪しいのはセグエのIDの部分ですね。

と言うわけでstoryboardエディターのInspector部分でフォルダアイコンのボタンから引っ張ったセグエのIDに、正しく(1文字も間違いなく、大文字小文字の違いも不可)あなたのソースコードと同じ`CompletedToDoItemsSegue`と言う文字が設定されているか確認してみてください。

[![セグエIDの確認](https://i.stack.imgur.com/cpsH7.jpg)](https://i.stack.imgur.com/cpsH7.jpg)

* * *

プログラミングの学習の際に、少しでも早く動くものを作りたいという気持ちはわからないでもないですが、もう少しきちんと理解しながら進めないと、ちょっとしたミスも自分で解決できず、かえって時間がかかることになります。着実に理解しながら進めるようにしてください。

ちなみに、ご質問内容にはstoryboardの設定等が書かれていないため一部推定が入っています。上記の確認で問題は見つからなかったという場合にはコメント等でお知らせください。

* * *

さらにご質問の内容には関係ないのですが、元コードのこの行:

[code]

    var toDoList: NSMutableArray = ["Go get groceries", "Walk the dog", "Watch a movie", "Do your homework"]
    
[/code]

Swiftでは、`NSArray`や`NSMutableArray`は出来るだけ使わないようにした方が、コードの他の部分が書きやすくなります。

[code]

    var toDoList: [String] = ["Go get groceries", "Walk the dog", "Watch a movie", "Do your homework"]
    
[/code]

また、上記よりも関連した修正が多くなりますが、2箇所の:

[code]

    var completedToDoList: [Int:String] = [:]
    
[/code]

も

[code]

    var completedToDoList: [String] = []
    
[/code]

としておいた方がいいでしょう。Dictionary型にする意味がほとんどない使い方をしています。


CC BY-SA 3.0
2017-01-04T16:51:47.733
31623
2017-01-05T03:36:17.917
2017-01-05T03:36:17.917
13972
13972
31621
answer