質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1163閲覧

TableViewCellをsegment controlを使い遷移したい

Ytan

総合スコア39

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/07/07 15:54

実現したいこと

cellをsegment controlのcase1からcase2に遷移したい.
遷移の仕方はcellを左スワイプしてdeleteとshareボタンでshareを押した時に行う。

ソースコード

Swift5

1import UIKit 2 3class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { 4 5 var todos:[Item] = [] 6 7 @IBOutlet weak var Table:UITableView! 8 9 @IBAction func segmentselected(_ sender: UISegmentedControl) { 10 11 switch sender.selectedSegmentIndex { 12 13 case 0: 14 let todo = todos 15 todo 16 case 1: 17 let todo2 = todos 18 todo2 19 default: 20 fatalError("case でカバーできていません") 21 } 22 Table.reloadData() 23 } 24 25 override func viewDidLoad() { 26 super.viewDidLoad() 27 // Do any additional setup after loading the view 28 Table.delegate = self 29 Table.dataSource = self 30 31// if UserDefaults.standard.object(forKey: "todoList") != nil{ 32// todos = UserDefaults.standard.object(forKey: "todoList") as! [Item] 33// } 34 //UD読み込み 35 if let data = UserDefaults.standard.data(forKey: "todoList"){ 36 self.todos = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! 37 [Item] 38 } 39 40 } 41 42 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 43 return todos.count 44 } 45 46 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 47 48 let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 49 let item = todos[indexPath.row] 50 cell.textLabel!.text = item.title 51 return cell 52 } 53 54 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 55 return view.frame.size.height/10 56 } 57 58 @IBAction func addNewTodo(_sender: Any){ 59 var textField = UITextField() 60 61 let alert = UIAlertController(title: "新しいTodoを追加", message: "", preferredStyle: .alert) 62 63 let action = UIAlertAction(title: "リストに追加", style: .default) { (action) in 64 65 let newItem:Item = Item(title: textField.text!) 66 print("追加されました") 67 68 //UD保存 69 let data = try! NSKeyedArchiver.archivedData(withRootObject: self.todos, requiringSecureCoding: false) 70 UserDefaults.standard.set(data, forKey: "todoList") 71 72 73 self.todos.append(newItem) 74 self.Table.reloadData() 75 } 76 77 alert.addTextField { (alertTextField) in 78 79 alertTextField.placeholder = "新しいTodo" 80 textField = alertTextField 81 } 82 83 alert.addAction(action) 84 present(alert, animated: true,completion: nil) 85 86 } 87 88 //swipe action 89 func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { 90 91 // シェアのアクションを設定する 92 let shareAction = UIContextualAction(style: .normal , title: "share") { 93 (ctxAction, view, completionHandler) in 94 print("シェアを実行する") 95 completionHandler(true) 96 } 97 // シェアボタンのデザインを設定する 98 let shareImage = UIImage(systemName: "square.and.arrow.up")?.withTintColor(UIColor.white, renderingMode: .alwaysTemplate) 99 shareAction.image = shareImage 100 shareAction.backgroundColor = UIColor(red: 0/255, green: 125/255, blue: 255/255, alpha: 1) 101 102 // 削除のアクションを設定する 103 let deleteAction = UIContextualAction(style: .destructive, title:"delete") { 104 (ctxAction, view, completionHandler) in 105 self.todos.remove(at: indexPath.row) 106 tableView.deleteRows(at: [indexPath], with: .automatic) 107 completionHandler(true) 108 tableView.reloadData() 109 } 110 111 // スワイプでの削除を無効化して設定する 112 let swipeAction = UISwipeActionsConfiguration(actions:[deleteAction, shareAction]) 113 swipeAction.performsFirstActionWithFullSwipe = false 114 115 return swipeAction 116 } 117} 118 119class Item: NSObject,NSCoding{ 120 121 var title:String 122 123 init(title:String) { 124 self.title = title 125 } 126 127 func encode(with coder: NSCoder) { 128 coder.encode(self.title,forKey: "title") 129 } 130 131 required init?(coder: NSCoder) { 132 self.title = coder.decodeObject(forKey: "title") as! String 133 } 134 135}

疑問点,不明な点

1.segmentedcontrolにUDで保存したItemを表示するにはどうすればできるのか。
まずそれは可能か。
(今のコードだとviewDidLoadにて読み込みを行なっており、新たに追加したcellは保存されません)

2.cellの遷移はPerformSegueで行えるのか

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

1.segmentedcontrolにUDで保存したItemを表示するにはどうすればできるのか。
まずそれは可能か。

実際に実現されたいことは、segmentedControlにUDで保存したItemを表示ではなく、**「segmentedControlで選択したデータをテーブルに表示したい」**ということではないでしょうか。

それであれば、基本的には過去のご質問

  • [Segmented Control tableView 切り替えについて教えてください。

](https://teratail.com/questions/270249)

と同じく、 tableView に表示したいデータを segmentedControl の切り替えに合わせて変更すればいいことになります。

過去の回答では

Swift

1@IBAction func segmentSelected(_ sender: UISegmentedControl) { 2 switch sender.selectedSegmentIndex { 3 case 0: 4 // 視聴前 5 selectedData = data 6 case 1: 7 // 試聴後 8 selectedData = data2 9 default: 10 // 到達しないはず 11 fatalError("case でカバーできていません") 12 } 13 14 // MARK: 再読み込み 15 tableView.reloadData() 16 }

としていましたが、これを「全てのToDo」「Shareで選択されたToDo」という具合に切り替えれば実現できます。

方法はいろいろ考えられますが、一つの案としては次のとおりです。

Swift

1 // tableView で実際に表示する ToDo 2 var todos:[Item] = [] 3 4 // 入力された ToDo 5 var allTodo: [Item] = [] 6 // 選択された ToDo 7 var selectedTodo: [Item] = []

という感じに変数を用意しておきます。

入力された、あるいは UserDefaults から復元したデータは allTodo に入れておきます。
また、シェアされた ToDo は selectedTodo に追加することになります。

SegmentedControl で切り替えを行った場合には

Swift

1 @IBAction func segmentselected(_ sender: UISegmentedControl) { 2 3 switch sender.selectedSegmentIndex { 4 case 0: 5 // 6 todos = allTodo 7 case 1: 8 // 9 todos = selectedTodo 10 default: 11 fatalError("case でカバーできていません") 12 } 13 Table.reloadData() 14 }

という具合に表示するデータを切り替えることになります。

基本的なアイデアは、tableView で表示するデータはtodos: [Item]に固定しておき(その方が tableView の各 delegate, dataSource の記述がシンプルだから)、表示したいデータに合わせて、その実体を allTodo: [Item]にするか、あるいはselectedTodo: [Item]にするのか切り替えておくという考え方です。

(今のコードだとviewDidLoadにて読み込みを行なっており、新たに追加したcellは保存されません)

それは、UserDefaults に保存したあと、配列に Item を保存しているためです。

Swift

1 let data = try! NSKeyedArchiver.archivedData(withRootObject: self.allTodo, requiringSecureCoding: false) 2 UserDefaults.standard.set(data, forKey: "todoList") 3 4 // この位置だと保存されない 5 self.todos.append(newItem)

となっていますが、これでは append する前の配列しか保存されません(一つ古いデータまでしか保存されない)。

そうではなくて、

Swift

1 // まず配列に保存する 2 self.allTodo.append(newItem) 3 4 // 次に保存する 5 //UD保存 6 let data = try! NSKeyedArchiver.archivedData(withRootObject: self.allTodo, requiringSecureCoding: false) 7 UserDefaults.standard.set(data, forKey: "todoList")

という具合にすれば、希望したタイミングで保存できます。

2.cellの遷移はPerformSegueで行えるのか

performSegueは viewController などの切り替えを設定した Segue に従って行うためのメソッドです。

そうではなくて、シェアを選択したときにシェア用の配列にデータを保存したいのではないでしょうか。

そうであれば、次のような変更が必要となります。

現状では

Swift

1 // シェアのアクションを設定する 2 let shareAction = UIContextualAction(style: .normal , title: "share") { 3 (ctxAction, view, completionHandler) in 4 print("シェアを実行する") 5 6 completionHandler(true) 7 }

このように、シェアボタンを押された時には何もおこなわれていませんが、そうではなくて

Swift

1 // シェアのアクションを設定する 2 let shareAction = UIContextualAction(style: .normal , title: "share") { 3 (ctxAction, view, completionHandler) in 4 print("シェアを実行する") 5 6 // 7 self.selectedTodo.append(self.allTodo[indexPath.row]) 8 9 completionHandler(true) 10 }

という具合に、シェアを選択したときにシェア用の配列にデータを追加するという操作が必要となります。

このような感じで、全体のコードとしては以下のような感じになります。ただし、このコードが最適なコードとは限りませんし、インタフェース的にも色々な問題を抱えています。ただ、それについては質問者さんが自分で使ってみながら問題点を把握し、修正された方が良いかと思いますので、是非問題点を見つけたら修正してみてください。

Swift

1import UIKit 2 3class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { 4 // MARK: tableView で実際に表示する ToDo 5 var todos:[Item] = [] 6 7 // MARK: 入力された ToDo 8 // TODO: プロパティオブザーバを使えば全体的な流れをもう少し簡潔に書けるが、それは先の話 9 var allTodo: [Item] = [] 10 // MARK: 選択された ToDo 11 var selectedTodo: [Item] = [] 12 13 @IBOutlet weak var Table:UITableView! 14 15 @IBAction func segmentselected(_ sender: UISegmentedControl) { 16 17 switch sender.selectedSegmentIndex { 18 19 case 0: 20 // 21 todos = allTodo 22 case 1: 23 // 24 todos = selectedTodo 25 default: 26 fatalError("case でカバーできていません") 27 } 28 Table.reloadData() 29 } 30 31 override func viewDidLoad() { 32 super.viewDidLoad() 33 // Do any additional setup after loading the view 34 Table.delegate = self 35 Table.dataSource = self 36 37 //UD読み込み 38 if let data = UserDefaults.standard.data(forKey: "todoList"){ 39 // 40 self.allTodo = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! 41 [Item] 42 } 43 } 44 45 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 46 return todos.count 47 } 48 49 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 50 51 let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) 52 let item = todos[indexPath.row] 53 cell.textLabel!.text = item.title 54 return cell 55 } 56 57 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 58 return view.frame.size.height/10 59 } 60 61 @IBAction func addNewTodo(_sender: Any){ 62 var textField = UITextField() 63 64 let alert = UIAlertController(title: "新しいTodoを追加", message: "", preferredStyle: .alert) 65 66 let action = UIAlertAction(title: "リストに追加", style: .default) { (action) in 67 68 let newItem:Item = Item(title: textField.text!) 69 print("追加されました") 70 71 // MARK: 順番が違う 72 // まず配列に保存する 73 self.allTodo.append(newItem) 74 75 // MARK: 次に保存する 76 // TODO: プロパティオブザーバに入れてしまう方法もある 77 // UD保存 78 let data = try! NSKeyedArchiver.archivedData(withRootObject: self.allTodo, requiringSecureCoding: false) 79 UserDefaults.standard.set(data, forKey: "todoList") 80 81 // MARK: この位置だと保存されない 82 //self.todos.append(newItem) 83 // MARK: コピーしなおし 84 // TODO: プロパティオブザーバに入れてしまう方法もあるが、それはそれで考えなければいけないことも増える 85 self.todos = self.allTodo 86 87 self.Table.reloadData() 88 } 89 90 alert.addTextField { (alertTextField) in 91 alertTextField.placeholder = "新しいTodo" 92 textField = alertTextField 93 } 94 95 alert.addAction(action) 96 present(alert, animated: true,completion: nil) 97 } 98 99 //swipe action 100 func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { 101 102 // シェアのアクションを設定する 103 let shareAction = UIContextualAction(style: .normal , title: "share") { 104 (ctxAction, view, completionHandler) in 105 print("シェアを実行する") 106 107 // MARK: シェア用の配列に追加 108 self.selectedTodo.append(self.allTodo[indexPath.row]) 109 110 completionHandler(true) 111 } 112 // シェアボタンのデザインを設定する 113 let shareImage = UIImage(systemName: "square.and.arrow.up")?.withTintColor(UIColor.white, renderingMode: .alwaysTemplate) 114 shareAction.image = shareImage 115 shareAction.backgroundColor = UIColor(red: 0/255, green: 125/255, blue: 255/255, alpha: 1) 116 117 // 削除のアクションを設定する 118 let deleteAction = UIContextualAction(style: .destructive, title:"delete") { 119 (ctxAction, view, completionHandler) in 120 // MARK: todos ではなくて allTodo から削除 121 //self.todos.remove(at: indexPath.row) 122 self.allTodo.remove(at: indexPath.row) 123 124 // MARK: 削除した結果も userDefaults に保存 125 // TODO: プロパティオブザーバに入れてしまう方法もある 126 let data = try! NSKeyedArchiver.archivedData(withRootObject: self.allTodo, requiringSecureCoding: false) 127 UserDefaults.standard.set(data, forKey: "todoList") 128 129 tableView.deleteRows(at: [indexPath], with: .automatic) 130 // MARK: コピーしなおし 131 // TODO: プロパティオブザーバに入れてしまう方法もあるが、それはそれで考えなければいけないことも増える 132 self.todos = self.allTodo 133 completionHandler(true) 134 } 135 136 // スワイプでの削除を無効化して設定する 137 let swipeAction = UISwipeActionsConfiguration(actions:[deleteAction, shareAction]) 138 swipeAction.performsFirstActionWithFullSwipe = false 139 140 return swipeAction 141 } 142} 143 144class Item: NSObject,NSCoding{ 145 146 var title:String 147 148 init(title:String) { 149 self.title = title 150 } 151 152 func encode(with coder: NSCoder) { 153 coder.encode(self.title,forKey: "title") 154 } 155 156 required init?(coder: NSCoder) { 157 self.title = coder.decodeObject(forKey: "title") as! String 158 } 159 160}

投稿2020/07/08 02:04

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ytan

2020/07/08 14:52

todosを大元として情報を変更する際に新たにプロパティを作るのですね。  上記のコードにshare後のcellの保存を新たなUDを作成しshareと保存の動作は満足のいくものとなりました。self.todos = self.allTodoの部分は新たに更新したallTodoのデータをtodosに格納しているということですか? 新たに出た問題点は 1このままだとcase1のcell(share後)のshareを押した際にcaseにcellが増えてしまう。 2delete機能が上手く作動しなくなりました。削除機能には問題なくシステム上削除はされているのですがcrashしてNumberOfRowsInSectionが無効とのことです。
TsukubaDepot

2020/07/08 20:06

> self.todos = self.allTodoの部分は新たに更新したallTodoのデータをtodosに格納しているということですか? そういうことになります。 > 1このままだとcase1のcell(share後)のshareを押した際にcaseにcellが増えてしまう。 segmentedC control で表示させる情報を切り替えても、スワイプ操作で出るshareの操作は同じなので、ここの処理について考え直す必要があるのではないでしょうか。 > 2delete機能が上手く作動しなくなりました。削除機能には問題なくシステム上削除はされているのですがcrashしてNumberOfRowsInSectionが無効とのことです。 具体的なエラーメッセージをみないと何とも言えませんが、表示させたいセルと表示しているセルに矛盾が生じていたりしないでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問