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

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

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

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

1回答

958閲覧

tableView.reloadData()をMVCのModelから実行したいのですが

Raleigh

総合スコア8

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2020/09/11 12:43

前提・実現したいこと

現在以下の動画のようなアプリを作っていて、そのコードをMVCで書き直しています。
そこで動画の最後の方のtableViewのセルをスワイプで削除したときに、一番上の合計獲得標高の更新をすることができずにいます。

発生している問題・エラーメッセージ

Modelでセルの生成と表示はできています。
スワイプで削除する操作もModelで行っていて、削除後にtableViewの更新をし合計獲得標高の値を変更することができません。ModelからtableViewの更新はできないので、Controllerで更新をするように伝えるような方法はないでしょうか?

該当のソースコード

Controller

1import UIKit 2import RealmSwift 3 4class RecordListViewController: UIViewController { 5 6 let realm = try! Realm() 7 8 private let dataSource = ElevationDataSource() 9 10 var model: Results<RealmData>? 11 12 @IBOutlet fileprivate weak var tableView: UITableView! { 13 didSet { 14 tableView.dataSource = dataSource 15 tableView.delegate = dataSource 16 17 tableView.register(UINib(nibName: "ElevationListTableViewCell", bundle: nil), forCellReuseIdentifier: "ElevationListTableViewCell") 18 tableView.register(UINib(nibName: "ElevationSumTableViewCell", bundle: nil), forCellReuseIdentifier: "ElevationSumTableViewCell") 19 } 20 21 } 22 23 24 override func viewDidLoad() { 25 super.viewDidLoad() 26 27 model = realm.objects(RealmData.self).sorted(byKeyPath: "id", ascending: true) 28 dataSource.setupModel(model) 29 } 30}

Model

1import UIKit 2import RealmSwift 3import SwipeCellKit 4 5class ElevationDataSource: NSObject { 6 7 let realm = try! Realm() 8 9 var model: Results<RealmData>? 10 11 var sum: Double = 0.0 12 13 func setupModel(_ model: Results<RealmData>?) { 14 self.model = model 15 sum = model?.sum(ofProperty: "elevation") ?? 0.0 16 } 17} 18 19extension ElevationDataSource: UITableViewDataSource, SwipeTableViewCellDelegate { 20 21 //スワイプした際に表示される文字や画像を設定。またタップした際の処理も設定 22 func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? { 23 guard orientation == .right else { return nil } 24 25 let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in 26 27 self.updateModel(indexPath) 28 } 29 30 deleteAction.image = UIImage(named: "delete-Icon") 31 32 return [deleteAction] 33 } 34 35 func updateModel(_ indexPath: IndexPath) { 36 if let elevationForDeletion = self.model?[indexPath.row] { 37 do { 38 try self.realm.write { 39 self.realm.delete(elevationForDeletion) 40 } 41 model = realm.objects(RealmData.self).sorted(byKeyPath: "id", ascending: true) 42 setupModel(model) 43// self.loadView() 44// self.viewDidLoad() 45// このあたりでtableViewを更新する操作をしたいです 46 } catch { 47 print("Error deleting elevation (error)") 48 } 49 } 50 } 51 52 func tableView(_ tableView: UITableView, editActionsOptionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions { 53 var options = SwipeOptions() 54 options.expansionStyle = .destructive 55 return options 56 } 57 58 func numberOfSections(in tableView: UITableView) -> Int { 59 2 60 } 61 62 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 63 if section == 0 { 64 return 1 65 } else { 66 return model?.count ?? 1 67 } 68 } 69 70 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 71 if indexPath.section == 0 { 72 guard let cell = tableView.dequeueReusableCell(withIdentifier: "ElevationSumTableViewCell", for: indexPath) as? ElevationSumTableViewCell else { 73 return UITableViewCell() 74 } 75 cell.bodyLabel.text = String(format: "%.2fm", sum) 76 return cell 77 } else { 78 guard let cell = tableView.dequeueReusableCell(withIdentifier: "ElevationListTableViewCell", for: indexPath) as? ElevationListTableViewCell else { 79 return UITableViewCell() 80 } 81 82 cell.delegate = self 83 84 if let elevation = model?[indexPath.row] { 85 cell.dateLabel.text = elevation.date 86 cell.bodyLabel.text = String(format: "%.2fm", elevation.elevation) 87 } 88 return cell 89 } 90 } 91} 92 93extension ElevationDataSource: UITableViewDelegate { 94 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 95 96 return 100 97 } 98} 99

試したこと

ModelでControllerのインスタンスを生成しさえすれば、簡単だと思うのですが。それだとMVCで書いている意味がなくなってしまうような気がして他の方法を探しましたが、見つかりませんでした。

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

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

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

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

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

guest

回答1

0

ベストアンサー

updateModeltableView のインスタンスを渡してあげれば実現できるのではないでしょうか。
コンパイルせずに書いているので不都合があるかもしれませんが、考え方としては次の通りです。

たとえば、

Swift

1 func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? { 2 guard orientation == .right else { return nil } 3 4 let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in 5 6 // ここ 7 self.updateModel(tableView, for: indexPath) 8 } 9 deleteAction.image = UIImage(named: "delete-Icon") 10 11 return [deleteAction] 12 }

という感じにして、updateModel

Swift

1 // 引数の個数とラベルを変更(ラベル名はお好みで) 2 func updateModel(_ tableView, for indexPath: IndexPath) { 3 if let elevationForDeletion = self.model?[indexPath.row] { 4 do { 5 try self.realm.write { 6 self.realm.delete(elevationForDeletion) 7 } 8 model = realm.objects(RealmData.self).sorted(byKeyPath: "id", ascending: true) 9 setupModel(model) 10// self.loadView() 11// self.viewDidLoad() 12// このあたりでtableViewを更新する操作をしたいです 13 // ここで更新 14 self.tablewVoew.reloadData() 15 } catch { 16 print("Error deleting elevation (error)") 17 } 18 } 19 }

こんな感じになるのではないでしょうか。

投稿2020/09/11 23:54

TsukubaDepot

総合スコア5086

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

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

Raleigh

2020/09/12 02:09

なるほど!tableViewが引数にあることに気づきませんでした!ありがとうございます。 ちなみにdelegateを使ってModelからViewControllerに更新を依頼する方法も思いついたのですが、どちらがMVC的な書き方として正しいのでしょうか? 頂いた回答のほうがコードは短くて済みそうですが
TsukubaDepot

2020/09/12 03:24

多くの例でも @IBOutlet で tableView, delegate や DataSource のメソッドの第一引数も tableView としてあるため、結構ここに気づかないことが多いような気がします。 私自身、MVC には詳しくないのでなんとも言えませんが、どれが正しいということはないかと思います。 ただ、現状では、RecordListViewController が tableView の Outlet を持っていて、一方 ElevationDataSource が tableView を直接操作しているので、正直なところ一瞬わかりにくいようにも思えます。Model の中で tableView という View を操作していますから、一瞬 Model の操作なのか、 View の操作なのか混乱してしまいそうです(頑張って作っているところ、申し訳ないのですが...)。 delegate を作る方法も一瞬思いついたのですが、現状のモデルだとメリットがあまりないような感じもします。tableView の操作は Model 側で行っているのに、tableView の更新を Controller で行わせるメリットがあまり感じられないためです。 適切にコメントできなくて申し訳ないです。
Raleigh

2020/09/12 06:10

改めて考えてみると、Modelがセルのテキストを操作しているのはおかしなことだと気づきました。 データを取得する部分はModelにまかせて、セルの操作やtableViewの操作はViewに任せるように改良します!助けてもらってばかりで、本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問