🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

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

Swift

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

Q&A

解決済

1回答

6476閲覧

modelの値を更新しても、親Viewのテキストが更新されない。 子ビューのみ更新される。

tabe_unity

総合スコア31

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

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

Swift

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

0グッド

0クリップ

投稿2019/12/28 04:26

わからないこと

modelの値を更新しても、親Viewのテキストが更新されない。
親Viewから更新しても、子Viewから更新されても、子Viewのテキストが更新されるだけで親Viewのテキストは更新されない

下記ソースコード内のコメントにあるように、

  1. 子View内から値を更新すると、子Viewのテキストは更新されるが親Viewのテキストは更新されない
  2. 親View内から値を更新しても、上記と同様

親Viewのテキストを自動的に更新するようにしたいです.
@Published したものは、更新されると自動的に通知をするようですが、何故かされない状況です.
よろしくお願いします.

環境

  • Xcode: 11.3
  • Swift: 5.1.3

ソースコード

swift

1import Foundation 2import SwiftUI 3 4//親View 5struct ContentView: View { 6 @ObservedObject var viewModel: ContentViewModel 7 8 var body: some View { 9 VStack { 10 List(viewModel.songs) { song in 11 SongCell(viewModel: self.viewModel, song: song) 12 } 13 14 //3. ここの値が更新されない(起動時のデフォルト値は"title1") 15 Text("I am ContetView : (viewModel.songs[0].title)") 16 17 //2. セルをタップ後(viewModel.songs[0].titleを"changed from ContentView"に更新) 18 //上にある3.のテキストは更新されない 19 Button(action: { 20 self.viewModel.songs[0].title = "changed from ContentView" 21 }, label: {Text("contentView add song")}) 22 } 23 } 24} 25 26//楽曲ごとのセル、子ビュー 27struct SongCell: View { 28 @ObservedObject var viewModel: ContentViewModel 29 @ObservedObject var song: Song 30 31 32 var body: some View { 33 HStack { 34 Text("(song.title) / (song.artist)") 35 Button(action: { 36 37 //1. セル内の表記のみ変化する(viewModel.songs[0].titleを更新) 38 // 親ビューの3.のテキストは更新されない 39 self.song.title = "changed from SongCell" 40 41 }, label: { 42 Text("button") 43 }) 44 } 45 } 46} 47 48//ViewModel 49class ContentViewModel: ObservableObject { 50 @Published var songs: [Song] = [ 51 Song.init(title: "title1", artist: "artist1", url: "url1"), 52 ] 53 54 func addSong(title: String, artist: String, url: String) -> Void { 55 let song = Song(title: title, artist: artist, url: url) 56 songs.append(song) 57 } 58 59 func titleChange(song: Song, newTitle: String) { 60 song.title = newTitle 61 } 62} 63 64 65//各曲のデータ 66class Song: Identifiable, ObservableObject{ 67 var id = UUID() 68 @Published var title: String 69 @Published var artist: String 70 @Published var url: String 71 72 init(title: String, artist: String, url: String) { 73 self.title = title 74 self.artist = artist 75 self.url = url 76 } 77} 78

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

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

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

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

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

guest

回答1

0

ベストアンサー

現象再現しました。
原因は、Songの変更が、ContentViewModelの変更とはみなされない為だと思います。
親viewはContentViewModelをみていて、子viewはSongsをみています。
あるSong.titleを変えたとき、Songは@Publishしているtitleの変化を通知するので子viewは変化を反映します。
しかし、親viewのみているContentViewModel.songsには変化が生じないため、ContentViewModelが変化したとはみなされず通知されていないのだと思います。
これはSongがクラスで参照型なので生じる現象です。
以下にSongを値型であるstructに変えた場合にどうなるかを例示します。
この実装なら、親viewも子viewも両方とも変化します。

swift

1import SwiftUI 2 3//親View 4struct ContentView1: View { 5 @ObservedObject var viewModel: ContentViewModel 6 7 var body: some View { 8 VStack { 9 List(viewModel.songs) { song in 10 SongCell(viewModel: self.viewModel, song: song) 11 } 12 13 Text("I am ContetView : (viewModel.songs[0].title)") 14 15 Button(action: { 16 self.viewModel.changeTitle(of: self.viewModel.songs[0], to: "changed from ContentView") 17 }, label: { Text("contentView add song") }) 18 } 19 } 20} 21 22//楽曲ごとのセル、子ビュー 23struct SongCell: View { 24 @ObservedObject var viewModel: ContentViewModel 25 var song: Song 26 27 var body: some View { 28 HStack { 29 Text("(song.title) / (song.artist)") 30 31 Button(action: { 32 self.viewModel.changeTitle(of: self.song, to: "changed from SongCell") 33 34 }, label: { 35 Text("button") 36 }) 37 } 38 } 39} 40 41//ViewModel 42class ContentViewModel: ObservableObject { 43 @Published var songs: [Song] = [ 44 Song.init(title: "title1", artist: "artist1", url: "url1"), 45 ] 46 47 func addSong(title: String, artist: String, url: String) -> Void { 48 let song = Song(title: title, artist: artist, url: url) 49 songs.append(song) 50 } 51 func changeTitle(of song: Song, to newTitle: String) { 52 guard let index = songs.firstIndex(where: { $0.id == song.id }) else { return } 53 let updatedSong = song.changingTitle(to: newTitle) 54 songs[index] = updatedSong 55 } 56} 57 58 59//各曲のデータ 60struct Song: Identifiable { 61 var id: UUID 62 var title: String 63 var artist: String 64 var url: String 65 66 init(title: String, artist: String, url: String) { 67 id = UUID() 68 self.title = title 69 self.artist = artist 70 self.url = url 71 } 72 73 init(id: UUID, title: String, artist: String, url: String) { 74 self.id = id 75 self.title = title 76 self.artist = artist 77 self.url = url 78 } 79 80 func changingTitle(to newTitle: String) -> Song { 81 return Song(id: id, title: newTitle, artist: artist, url: url) 82 } 83 84 func changingArtist(to newArtist: String) -> Song { 85 return Song(id: id, title: title, artist: newArtist, url: url) 86 } 87 88 func changingUrl(to newUrl: String) -> Song { 89 return Song(id: id, title: title, artist: artist, url: newUrl) 90 } 91}

既存のコードに対しては大きな変更になるでしょうし、変更がある度に作り直すのはパフォーマンス上も気になってしまうかもしれませんが、appleは値型によるコーディングを推奨していますし、このほうがSwiftUIとも相性がいいと思います。

もっとよい実装もありそうな気もしますが・・・他の方の回答に期待します。

投稿2019/12/30 15:46

eytyet

総合スコア803

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

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

tabe_unity

2019/12/31 02:19

ありがとうございます。無事動作を確認することができました。 やはり親Viewが検知できていなかったんですね。確かにsongsそのものに変化はないですね。次から値型のプログラミングを意識していきます! 詰まって進まなかったので助かりました! これで開発を再開することができます! ありがとうございました!
tabe_unity

2019/12/31 02:35

すいません、一つ質問があります。 Song struct内にて定義されている関数で、返されるSongのアップデートされないプロパティは元のプロパティがそのまま入るという解釈で大丈夫でしょうか?
eytyet

2019/12/31 06:56

はい。idも含めて元の値がそのまま維持されます。
tabe_unity

2019/12/31 14:18

ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問