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

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

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

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

Swift

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

Q&A

0回答

1076閲覧

swiftUIにて獲得したはずのアイテムが何度も再生産される

dismail

総合スコア0

iOS

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

Swift

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

0グッド

0クリップ

投稿2021/04/18 07:57

SwiftUIにおけるインスタンスの状態維持

現在SwiftUIにて脱出ゲームを作成しています。
基本的な機能を実装しているところなのですが、アイテムを獲得後に(isTakenがtrueになると)画面下のアイテムドックにアイテムが格納され、そしてそのアイテムをタップして選択すると(isSelectedがtrueになると)画面に再び同じアイテムが再生産されてしまいます。画面は東西南北に移動できるのですが、アイテムを選択した状態で別の画面に移動し、また同じ方角の画面に戻ってくるとこの現象が起きます。アイテムが選択されていない時は起こりません。結果的に同じアイテムを無限に獲得できでしまっている状態です。

view内の監視されている変数が変更されると自動的にviewが再描画されるとのことですが、
ItemObjectはシングルトンでインスタンスが固定されるようにしているのですが、なぜこのようなことになるのかがわかりません。

該当のソースコード

swift

1import Foundation 2 3 4struct Tool : Equatable { 5 let id : Int 6 var text: String 7 var image: String 8 var isTaken: Bool 9 var isSelected: Bool 10} 11 12 13 14class ItemObject: ObservableObject { 15 // for singleton 16 static let shared = ItemObject() 17 private init(){} 18 19 @Published var item1 = Tool(id: 1, text: "This is item 1", image: "item1.png", isTaken: false, isSelected: false) 20 @Published var item2 = Tool(id: 2, text: "This is item 2", image: "item2.png", isTaken: false, isSelected: false) 21 @Published var item3 = Tool(id: 3, text: "This is item 3", image: "item3.png", isTaken: false, isSelected: false) 22 23 24 // empty array 25 @Published var itemArray: [Tool] = [] 26 27 28} 29 30

swift

1import SwiftUI 2 3struct WindowTool: View { 4 5 @ObservedObject var itemData = ItemObject.shared 6 7 @State var putItem : Tool 8 @State var Width : CGFloat 9 @State var Height : CGFloat 10 @State var X : CGFloat 11 @State var Y : CGFloat 12 13 var body: some View { 14 if !itemData.itemArray.contains(putItem) && !putItem.isTaken { 15 { 16 Image(putItem.image).resizable() 17 }() 18 .frame(minWidth: 0 ,maxWidth: Width, minHeight: 0 ,maxHeight: Height) 19 .offset(x: X, y: Y) 20 .onTapGesture { 21 addItem(item: putItem) 22 putItem.isTaken = true 23 } 24 } 25 } 26 // to add an item 27 func addItem(item: Tool) { 28 itemData.itemArray.append(item) 29 } 30 31}

swift

1import SwiftUI 2 3struct Window: View { 4 5 @ObservedObject var windowObject = WindowObject.shared 6 @ObservedObject var itemData = ItemObject.shared 7 8 var body: some View { 9 ZStack{ 10 BackGroundPicture(imageFilename: windowObject.window.image) 11 WindowTool(putItem: itemData.item1, Width: 100, Height: 100, X: 0, Y: 0) 12 } 13 14 } 15 16} 17 18 19 20 21

ちなみにWindowObjectもItemObjectと全く同じような仕様で作成されています。

試したこと

ちなみにWindowToolのviewのputItemの変数の部分にそれぞれitemData.item1とそのままベタ書きするとこの問題は起きません。つまり、アイテムが獲得された状態が維持されています。

シングルトンで書く前は全て@EnvironmentObjectにしていました。ですが、状況は全く変わっていません。
どのような対処方法が考えられるでしょうか。再描写されるタイミングがイマイチ判断できません。

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

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

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

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

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

hoshi-takanori

2021/04/19 00:28

isTaken が true なら取得済みなので addItem を呼ばなければ良いのでは。
dismail

2021/04/19 03:53 編集

ご指摘ありがとうございます。 isTakenは単に画面から削除するためだけに存在するフラグで、 アイテムドックは配列を参照して獲得アイテムを表示しているので、 該当コードからaddItemを削除しても、アイテムドックにアイテムが表示されないのみで画面遷移から戻ると同じようにアイテムが復活してしまいます。 おそらくwindowが再描写されるたびにWindowTool(putItem: itemData.item1, ~~)のVIewが呼ばれるので、インスタンス生成でいうところのnewのようなメソッドが繰り返し連続して呼ばれていることが原因であると見ているのですが、WindowのView側似て WindowToolの内容をベタ書きする以外では、別のindexなどを設定してif index == false {}の中にWindowTool()を書くとしても煩雑になって、WindowToolを切り分けるメリットが薄まるような気がしています。 WindowToolの引数に以前と同じものが入っていた場合、上書きされず、以前の設定が維持されるような@State以外の書き方がないか模索しているところです。
hoshi-takanori

2021/04/19 06:31

たぶん、WindowTool の putItem が単なる @State なので、構造体がコピーされるからですね。 @Binding にして $itemData.item1 とかを渡せば良いかも。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問