

## 実現したいこと

VieControllerにあるTableVieのcellをタップするとcellに入っている情報をRegisterViewControllerで表示することは前までの質問でできたのですが、新たにListViewControllerを作りRegisterViewControllerの右上のボタンをタップするとListViewControllerのcellに表示したいです。

またViewControllerとListViewControllerはTabBar移動をできるようにしています。  
RegisterViewはいわばViewControllerの付属機能となっているのでListViewControllerからNavigationへ戻ることもしたくないと思っているため、登録したらListViewControllerに情報の入ったcellは追加されるも遷移せずにそのままViewControllerにのみ戻れるようにしたいです。

## 疑問点

遷移なしで値を渡すのでUDで値のみ受け渡すですが、CustomCellのUDなのでNSCodingを使用したいと思っているので、NSKeyedUnarchiverを使用したいです。  
RegisterのitemをaddListTappedされたらListViewのlistに入れてTableViewcellのlistNameLabelとlistDateLabelをxibで作っているのでそれに表示したいのですが、候補に出てこないので書き方を教えてもらいたいです。

## ソースコード

**ItemModelClass**

[code]

    class Item : NSObject,NSCoding{
        
        var name:String
        var date:String
        
        init(name:String,date:String){
            self.name = name as String
            self.date = date as String
        }
        
        func encode(with coder: NSCoder) {
            coder.encode(self.name,forKey: "name")
            coder.encode(self.date,forKey: "date")
        }
        
        required init?(coder: NSCoder) {
            self.name = coder.decodeObject(forKey: "name") as! String
            self.date = coder.decodeObject(forKey: "date") as! String
        }
    }
    
[/code]

**ViewController**

[code]

    class ViewController: UIViewController {
    
        @IBOutlet weak var table:UITableView!
        var items:[Item] = [Item]()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            table.register(TableViewCell.nib(), forCellReuseIdentifier: TableViewCell.identifier)
    
            table.delegate = self
            table.dataSource = self
            
            self.setupItems()
        }
        
        func setupItems(){
            items = [Item(name:"a",date:"1"),Item(name:"b",date:"2"),Item(name:"c",date:"3"),Item(name:"d",date:"4"),Item(name:"e",date:"5"),]
        }
    }
    以下省略
    
[/code]

**RegisterViewController**

[code]

    class RegisterViewController: UIViewController {
        
        @IBOutlet weak var registeredNameLabel: UILabel!
        @IBOutlet weak var registeredDateLabel: UILabel!
        @IBOutlet var addListButton:UIBarButtonItem!
        
        //Item型
        var item: Item!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            //MovieListに追加するためのボタン
            addListButton = UIBarButtonItem(title: "リストへ追加", style: .plain, target: self, action: #selector(addListTapped(_:)))
            self.navigationItem.rightBarButtonItems = [addListButton]
            
            registeredNameLabel.text = item.name
            registeredDateLabel.text = item.date
        }
        
        @objc func addListTapped(_ sender: UIBarButtonItem){
            print("Tapped")
            //疑問点
    }
    
[/code]

**ListViewController**

[code]

    class ListViewController: UIViewController{
        
        var list:[Item] = []
        var allList:[Item] = []
        var selectedList:[Item] = []
        
        @IBOutlet weak var table:UITableView!
        
        @IBAction func segmentSelcted(_ sender: UISegmentedControl) {
            
            switch sender.selectedSegmentIndex {
            case 0:
                list = allList
            case 1:
                list = selectedList
            default:
                fatalError("caseでカバーできません")
            }
            table.reloadData()
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            table.delegate = self
            table.dataSource = self
            
            //UD読み込み
         
        }
    }
    extension ListViewController:UITableViewDelegate,UITableViewDataSource{
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            list.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: ListTableViewCell.identifier, for: indexPath) as! ListTableViewCell
            cell.setcell(item: list[indexPath.row])
            return cell
        }
    }
    
[/code]

ListTableViewCell

[code]

    func setcell(item:Item){
            self.listNameLabel.text = item.name as String
            self.listDateLabel.text = item.date as String
        }
    
[/code]


CC BY-SA 4.0
2020-08-08T13:23:51.543
69368
2020-08-09T16:13:46.403
2020-08-09T16:13:46.403
41235
41235
question
swift ios
UserDefaultsで保存した値を別のViewで読み込みたい


さて、コメントに書いたようにSingletonを作るのは特に難しいことではないため、「UserDefaultsで保存」と明記された質問に対する回答としては押し売り気味になりますが、

  * `UserDefaults`は使用せずにSingletonを利用する

と言う方針での回答を書かせていただきます。

* * *

さて、iOSプログラミングを始めてまだ間がない方にとって、(`UIViewController`や`UIView`以外の)自前のクラスを定義すると言うのは、理解の追いつかない難しいことに見えるとおっしゃる方は多いのですが、習うより慣れろでやってみると、そう難しいことではないことがわかります。

基本はこんな形式のクラスを書くだけです。

[code]

    class MyData {
        //`UserDefaults`に合わせた名前のシングルトンを定義
        static let standard = MyData()
    
        //その他必要な機能をここに記述する
        //...
    }
    
[/code]

コメントを読む限り、

  * `RegisterViewController`で`ListViewController`に表示されるデータを操作

することが主眼のように見えるので、ここでは`ListViewController`の`allList`と`selectedList`を`MyData`に移してしまうことにします。

[code]

    class MyData {
        //`UserDefaults`に合わせた名前のシングルトンを定義
        static let standard = MyData()
    
        //複数のview controllerからアクセスされる変数は関連性の強い変数とともにこの中にまとめる
        private(set) var allList:[Item] = []
        private(set) var selectedList:[Item] = []
        
        func add(_ item: Item, isSelected: Bool = false) {
            allList.append(item)
            if isSelected {
                selectedList.append(item)
            }
        }
        
        //`allList`の一部を`selectedList`に入れるなんて操作が必要ならこちらに定義する
        //...
    }
    
[/code]

  * `private(set)`が付けてあるのは、いろんなところで勝手に配列操作を行われると整合性が取れなくなる可能性があるためです。

  * Singletonを表すプロパティ名は`shared`とか`default`とかが人気(?)だったりしますが、同じくSingletonを使っている`UserDefaults`の使用には抵抗が無いようなので、それに合わせて`standard`にしてあります。

  * コンピュータ上で扱える情報はなんでも「data」なんで、`MyData`なんて言うのはかなり最低なネーミングセンスです。実際のアプリではもうちょっとマシな名前にしてください。

* * *

上記の`MyData`を使うと`RegisterViewController`は次のようになります。

  * 削除(して移動)すべきところが分かりやすくなるようにコメントアウトしたままにしている部分がありますが、実際にお試しになるなら不要なんでサクッと削除してください。

[code]

    class RegisterViewController: UIViewController {
        
        @IBOutlet weak var registeredNameLabel: UILabel!
        @IBOutlet weak var registeredDateLabel: UILabel!
        @IBOutlet var addListButton:UIBarButtonItem!
        
        //Item型→可能な限りImplicitly Unwrappedにすることは避ける
        var item: Item? //<-
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            //MovieListに追加するためのボタン
            addListButton = UIBarButtonItem(title: "リストへ追加", style: .plain, target: self, action: #selector(addListTapped(_:)))
            self.navigationItem.rightBarButtonItems = [addListButton]
            
            //他画面から渡ってきたデータの処理は`viewDidLoad`でなく、`viewWillAppear`で行う方が良い
    //        registeredNameLabel.text = item?.name
    //        registeredDateLabel.text = item?.date
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            
            guard let item = item else {
                print("item is nil")
                //必要なら他のエラー処理
                return
            }
            
            //他画面から渡ってきたデータの処理はここで行う
            registeredNameLabel.text = item.name
            registeredDateLabel.text = item.date
        }
        
        @objc func addListTapped(_ sender: UIBarButtonItem){
            print("Tapped")
            //疑問点
            guard let item = item else {
                print("item is nil")
                //必要なら他のエラー処理
                return
            }
            //`MyData`のメソッドを通じてのみデータ操作を行う
            MyData.standard.add(item, isSelected: false)
        }
    }
    
[/code]

  * `item`のデータ型を`Item!`から`Item?`に変更してあります。`@IBOutlet`のような定形パターンを除いて、「Implicitly Unwrapped Optional(`!`)は使用しない」と覚えておくことをお勧めしておきます。

* * *

同じく、`ListViewController`を`MyData`使用版に差し替えるとこんな感じになります。

[code]

    class ListViewController: UIViewController {
        
        //変更があるたびに配列のコピーをするのではなく、どちらか必要な配列を都度「計算」してやる
        var list: [Item] {
            switch segmentedControl.selectedSegmentIndex {
            case 0:
                return MyData.standard.allList
            case 1:
                return MyData.standard.selectedList
            default:
                fatalError("caseでカバーできません")
            }
        }
        //複数のview controllerからアクセスされる変数は関連性の強い変数とともに`MyData`に移動させる
    //    var allList:[Item] = []
    //    var selectedList:[Item] = []
        
        @IBOutlet weak var table: UITableView!
        //`list`を計算型プロパティにするため、IBOutletを追加、正しく接続しておく
        @IBOutlet weak var segmentedControl: UISegmentedControl!
        
        @IBAction func segmentSelcted(_ sender: UISegmentedControl) {
            //`list`を計算型プロパティにすることで、ここで配列のコピーが不要になる
    //        switch sender.selectedSegmentIndex {
    //        case 0:
    //            list = allList
    //        case 1:
    //            list = selectedList
    //        default:
    //            fatalError("caseでカバーできません")
    //        }
            //しかし、`table.reloadData()`は必要
            table.reloadData()
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            table.delegate = self
            table.dataSource = self
            
            //必要なデータは全部`MyData`に入れてあるので、この時点で読み込んだりする必要はない
        }
        
        //`viewWillAppear(_:)`の中で`table.reloadData()`を呼ばないと画面が更新されない場合がある
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            
            table.reloadData()
        }
    }
    
[/code]

  * 上記の中で、`list`を計算型プロパティに書き換えている部分は必須ではありませんし、「どんな観点で見てもbetter」とは言い切れないのですが、「複数の配列を整合性を保って更新しなければいけない」と言う状態はプログラムの規模が大きくなるにつれ管理が非常に困難になってくるので、あえて入れさせていただきました。

* * *

ご質問中に示されたコードで省略されている部分については、この回答でも省略しましたが、それほど大きな修正は必要ないだろうと思うのですが、いかがでしょうか。

もしかしたら`UserDefaults`の使用必須と言う縛りの課題なのかもしれませんが、そうでなければ是非お試しいただきたいと思います。


CC BY-SA 4.0
2020-08-09T12:40:10.080
69393
2020-08-09T12:49:02.800
2020-08-09T12:49:02.800
13972
13972
69368
answer