前提・実現したいこと
SwiftUIにて、とある処理の結果一覧をListにて表示する画面を作っています。(画像①を参考)
別途用意した絞り込みウインドウにて検索条件を指定すると、結果一覧の表示内容が絞り込み条件によってリアルタイムに変わるようにしたいです。
おまけとして、Listのセルをタップするとその結果について詳細が見られる詳細ウィンドウを別途開く機能もついています。
仕組みとしては、フィルタリング済みの結果一覧配列をStateとして画面内に宣言しておき、Listではその配列を元に描画を行うことで表示の更新を行うオーソドックスな思想です。
発生している問題・エラーメッセージ
絞り込み条件を指定して結果配列をフィルタリングするまではうまく行ったのですが、
フィルタリング済みの結果配列を使ってListを再描画する部位に以下の問題点が発生しました。
1.フィルタリングしてもすぐにはListの表示内容が更新されない。
画像②は処理結果が「NG」である結果レコードのみを絞り込んだ状態です。
絞り込みを行ったため左上に表示されている件数表示は変化しており、各セルをタップしたときに表示される詳細ウィンドウの内容も正しいので内部的には絞り込み済み結果配列データは正しく更新されているようですが、見た目だけが更新されない状態となっています。
(なお、リストの表示される長さだけは更新されます。画像②の場合ですと、見た目のリストは9件目でぶった切られている状態です)
2.ただし、List内のセルに再描画がかかると見た目も更新される
画像③は処理結果「NG」も「OK」も除外したため、フィルタリング結果が0件となっている状態です。
0件であるためリストには何も表示されておりません。
ここから改めて、処理結果が「NG」である結果レコードのみを絞り込むと、画像④のように今度は見た目も正しく更新されます。
(なお、再描画がかかれば何でも良いらしく、リストをスクロールして見た目が更新されてないセルを見えない部位に追いやってから、改めてスクロールしてリストの位置をもとに戻すと、先程まで見た目が更新されていなかったセルも更新されます。)
↓画像③:一旦0件となるようにフィルタリングしてリストを消す
リスト内セルの再描画をきちんと行うにはどうすればよいのでしょうか?
該当のソースコード
Swift
1import SwiftUI 2 3struct ResultHeadListView: View { 4 //全結果リストが格納されている配列 5 @Binding var resultList: [ResultRow] 6 //絞り込み済みの結果リストが格納されている配列 7 @State var filteredResults: [ResultRow] = [ResultRow()] 8 //詳細ウィンドウ表示フラグ 9 @State var isModalResultShowing: Bool = false 10 //絞り込みウィンドウ表示フラグ 11 @State var isModalSearchViewShowing: Bool = false 12 //リストのタップ位置を記憶しておくメンバ 13 @State var tappedListIdx: Int = 0 14 //検索条件データをObservedとして子ウィンドウから受け取る 15 @ObservedObject var searchCondtion: SearchResultConditions = SearchResultConditions() 16 17 var body: some View { 18 19 20 VStack { 21 22 HStack { 23 //現在表示中の件数を表示 24 Text("(self.filteredResults.count)件表示中 (全(self.resultList.count)件)") 25 .frame(maxWidth: .infinity, alignment: .leading) 26 .padding(EdgeInsets(top: 20, leading: 20, bottom: 10, trailing: 20)) 27 28 //絞り込みボタン配置 29 Button(action: { 30 //絞り込みウインドウ表示フラグをON 31 isModalSearchViewShowing = true 32 }, label: { 33 34 VStack { 35 Image(systemName: "magnifyingglass") 36 .resizable() 37 .frame(width: 30,height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) 38 } 39 40 41 }) 42 .frame(maxWidth: .infinity, alignment: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/) 43 .padding(EdgeInsets(top: 20, leading: 20, bottom: 10, trailing: 20)) 44 .sheet(isPresented: $isModalSearchViewShowing, onDismiss: { 45 //シートを閉じた際に条件抽出を行う 46 self.filteringResults() 47 48 }, content: { 49 //絞り込みウインドウに検索条件をEnvironment参照として与えてモーダル表示 50 ResultSearchView().environmentObject(self.searchCondtion) 51 }) 52 } 53 54 //絞り込み済み結果配列を使ってリストを表示 55 List { 56 ForEach (0..<self.filteredResults.count, id: .self) { idx in 57 ResultRowView(result: self.filteredResults[idx]) 58 .id(idx) 59 .onTapGesture { 60 //リストタップ時には詳細ウィンドウをモーダル表示するようにする 61 isModalResultShowing = true 62 //タップされた位置を記憶する 63 self.tappedListIdx = idx 64 } 65 .sheet(isPresented: $isModalResultShowing, content: { 66 //タップ時の詳細ウィンドウ表示 67 ResultModalWindowView( 68 resultList: self.$filteredResults[self.tappedListIdx]) 69 }) 70 71 } 72 } 73 } 74 .onAppear(perform: { 75 //初回表示時には全結果をそのまま表示内容とするためにコピーする 76 self.filteredResults = self.resultList 77 }) 78 79 } 80 81 private func filteringResults() { 82 //検索条件で検索する 83 self.filteredResults = self.resultList.filter{ record in 84 85 ~~~検索条件~~~ 86 87 } 88 } 89} 90
補足情報(FW/ツールのバージョンなど)
Xcode:Ver.12.3
シミュレータiOS:14.3
Swift:Ver.5.3.2
あなたの回答
tips
プレビュー