環境
Xcode: 12.4
UI: SwiftUI
質問内容
ViewからViewModelを@ObservedObjectとして参照し、そのViewModelにて@Publishedなプロパティがあるのですが、そのプロパティの変更通知がViewにされないです。
以下に詳細を記載します。
(関連クラスなど)
- PresentAlert:
Viewです。
- PresentAlertViewModel
PresentAlertへバインドするViewModelです。
- AlertProvider
アラートダイアログの表示データ、表示可否情報をもつ管理するクラスです。
(状況の説明)
PresentAlertがPresentAlertViewModelをもち、
さらにPresentAlertViewModelがAlertProviderを保持しています。
swift
1 2import SwiftUI 3 4struct PresentAlertBase: View { 5 var body: some View { 6 7 VStack { 8 PresentAlert() 9 } 10 } 11} 12 13struct PresentAlert: View { 14 15 @ObservedObject var presentAlertVM = PresentAlertViewModel() 16 17 var body: some View { 18 19 VStack{ 20 Button("Plese Tap") { 21 presentAlertVM.showAlert() 22 } 23 .alert(isPresented: $presentAlertVM.alertProvider.shouldShowAlert) { 24 guard let alert = presentAlertVM.alertProvider.alert else { fatalError("????: Alert not available") } 25 return Alert(alert) 26 } 27 } 28 } 29} 30 31final class PresentAlertViewModel: ObservableObject { 32 @Published var alertProvider = AlertProvider() 33 34 func tapButton() { 35 showAlert() 36 } 37 38 func showAlert() { 39 alertProvider.alert = AlertProvider.Alert( 40 title: "demo", 41 message: "demo-message", 42 primaryButtomText: "OK", 43 primaryButtonAction: {}, 44 secondaryButtonText: "" 45 ) 46 } 47} 48 49final class AlertProvider: ObservableObject { 50 struct Alert { 51 var title: String 52 let message: String 53 let primaryButtomText: String 54 let primaryButtonAction: (() -> Void)? 55 let secondaryButtonText: String 56 } 57 58 /// アラートを表示するかどうかを示すフラグ。Viewが監視する。 59 @Published var shouldShowAlert = false 60 61 var alert: Alert? = nil { 62 didSet { 63 shouldShowAlert = alert != nil 64 } 65 } 66} 67
このとき、期待する動作は
「ボタンタップした際に、ViewModelのalertProviderへAlertが格納され、shouldShowAlertがtrueになりそれがViewに伝搬されてダイアログが表示される」
ということです。
が、表示されません。
shouldShowAlertがtrueになる、というところまでは確認できているのですが、その後なぜViewまで伝搬されていないのかがわかっていません。
また、例えばAertProviderをViewのプロパティとして保持した場合には問題なく動作することも確認できています。
ボタンをタップしてダイアログを表示する、ということ自体は実装方針を変更すれば実現は問題ないのですが、なぜこのようなことが生じているかが気になり質問させて頂きました。
質問対象の全コード
問題が発生する全体のコードです。ご参考頂けると幸いです。
swift
1 2import SwiftUI 3 4struct PresentAlertBase: View { 5 var body: some View { 6 7 VStack { 8 PresentAlert() 9 } 10 } 11} 12 13struct PresentAlert: View { 14 15 @ObservedObject var presentAlertVM = PresentAlertViewModel() 16 17 var body: some View { 18 19 VStack{ 20 Button("Plese Tap") { 21 presentAlertVM.showAlert() 22 } 23 .alert(isPresented: $presentAlertVM.alertProvider.shouldShowAlert) { 24 guard let alert = presentAlertVM.alertProvider.alert else { fatalError("????: Alert not available") } 25 return Alert(alert) 26 } 27 } 28 } 29} 30 31final class PresentAlertViewModel: ObservableObject { 32 @Published var alertProvider = AlertProvider() 33 34 func tapButton() { 35 showAlert() 36 } 37 38 func showAlert() { 39 alertProvider.alert = AlertProvider.Alert( 40 title: "demo", 41 message: "demo-message", 42 primaryButtomText: "OK", 43 primaryButtonAction: {}, 44 secondaryButtonText: "" 45 ) 46 } 47} 48 49final class AlertProvider: ObservableObject { 50 struct Alert { 51 var title: String 52 let message: String 53 let primaryButtomText: String 54 let primaryButtonAction: (() -> Void)? 55 let secondaryButtonText: String 56 } 57 58 /// アラートを表示するかどうかを示すフラグ。Viewが監視する。 59 @Published var shouldShowAlert = false 60 61 var alert: Alert? = nil { 62 didSet { 63 shouldShowAlert = alert != nil 64 } 65 } 66} 67 68extension Alert { 69 init(_ alert: AlertProvider.Alert) { 70 71 if !alert.primaryButtomText.isEmpty && !alert.secondaryButtonText.isEmpty { 72 self.init(title: Text(alert.title), 73 message: Text(alert.message), 74 primaryButton: .default(Text(alert.primaryButtomText), 75 action: alert.primaryButtonAction), 76 secondaryButton: .cancel(Text(alert.secondaryButtonText))) 77 } else { 78 79 self.init( 80 title: Text(alert.title), 81 message: Text(alert.message), 82 dismissButton: .default(Text(alert.primaryButtomText)) 83 ) 84 } 85 } 86} 87 88#if DEBUG 89struct PresentAlert_Previews: PreviewProvider { 90 static var previews: some View { 91 PresentAlertBase() 92 } 93} 94#endif
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/09/12 05:20 編集