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

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

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

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

Q&A

解決済

2回答

1811閲覧

【SwiftUI】@AppStorageと@Bindingを同時に使いたい

KaoruYoshida

総合スコア36

Swift

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

0グッド

0クリップ

投稿2022/08/06 03:31

前提

カレンダーで選択した日付をday(Date型)として読み取り、その日付と今日の日付との差を自動的に計算し表示するシステムを作っております。
そして現在選択した日付selectedDateを@AppStorageで保存するコードを書いています。

実現したいこと

  • 一度アプリをシャットダウンしても選択した日付、dayが保存されているようにしたい

発生している問題

選択した日付、selectedDateを保存したいのですが、すでに
@Binding var selectedDate: Date
と定義しているため、@AppStorageを追加する訳にもいかず、困っているという状況です。

初歩的な質問だと思うのですが、お分かりになる方、回答よろしくお願いします。

該当のソースコード

CalendarView.swift

1import SwiftUI 2import FSCalendar 3import UIKit 4 5struct CalendarTestView: UIViewRepresentable { 6 @Binding var selectedDate: Date 7 func makeUIView(context: Context) -> UIView { 8 9 typealias UIViewType = FSCalendar 10 11 let fsCalendar = FSCalendar() 12 13 fsCalendar.delegate = context.coordinator 14 fsCalendar.dataSource = context.coordinator 15 16 return fsCalendar 17 } 18 19 func updateUIView(_ uiView: UIView, context: Context) { 20 } 21 22 func makeCoordinator() -> Coordinator{ 23 return Coordinator(self) 24 } 25 26 class Coordinator: NSObject, FSCalendarDelegate, FSCalendarDataSource { 27 var parent:CalendarTestView 28 29 init(_ parent:CalendarTestView){ 30 self.parent = parent 31 } 32 33 func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { 34 // ***** 選択した日付をバインディングのselectedDateへ設定します 35 parent.selectedDate = date 36 } 37 38 } 39} 40 41struct CalendarApealView: View{ 42 @State private var date: Date = Date() 43 @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode> 44 @Binding var day: String 45 46 @State var onSwitch = false 47 @State var text = "hello" 48 var differString: String { 49 50 let calendar = Calendar.current 51 //today1は今日の日付 52 let today1 = Date() 53 let today2 = calendar.dateComponents([.year, .month, .day], from: today1) 54 let today3 = calendar.date(from: DateComponents(year: today2.year, month: today2.month, day: today2.day))! 55 let date2 = calendar.dateComponents([.year, .month, .day], from: date) 56 let date3 = calendar.date(from: DateComponents(year: date2.year, month: date2.month, day: date2.day))! 57 let result = calendar.dateComponents([.day], from: date3, to: today3) 58 return "\(result.day!)" 59 } 60 func set(_: Any?, forKey:String){ 61 62 } 63 var body: some View { 64 HStack{ 65 VStack{ 66 Text("When did you start this food style?") 67 .font(.title2) 68 CalendarTestView(selectedDate: $date) 69 .frame(height: 400) 70 HStack(){ 71 Text(differString) 72 .font(.title) 73 .padding() 74 .foregroundColor(Color(red: 0.324, green: 0.758, blue: 0.49)) 75 Text("Days") 76 .font(.title2) 77 } 78 79 Button(action: { 80 //ここでdayに値代入! 81 day = differString 82 UserDefaults.standard.set(self.differString, forKey: "String") 83 func StringToDate(dateValue: String, format: String) -> Date { 84 let dateFormatter = DateFormatter() 85 dateFormatter.calendar = Calendar(identifier: .gregorian) 86 dateFormatter.dateFormat = format 87 return dateFormatter.date(from: dateValue) ?? Date() 88 } 89 date = StringToDate(dateValue: differString, format: "yyyy/MM/dd") 90 self.onSwitch.toggle() 91 self.presentationMode.wrappedValue.dismiss() 92 }) { 93 Text("confirm") 94 .font(.title2) 95 .padding(16) 96 .background(Color(red: 0.324, green: 0.758, blue: 0.49)) 97 .foregroundColor(Color.white) 98 .cornerRadius(10) 99 } 100 } 101 }.navigationTitle("Day Setting") 102 } 103} 104 105struct CalendarTestView_Previews: PreviewProvider { 106 static var previews: some View { 107 CalendarApealView(day: .constant("12")) 108 } 109}

補足情報(FW/ツールのバージョンなど)

macOS バージョン12.3.1
2.6 GHz 6コアIntel Core i7
Xcodeバージョン13.3.1

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

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

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

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

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

guest

回答2

0

@State の宣言をそのまま @AppStorage に置き換えるだけで可能なはずなので試してみてください。

(UserDefaultはデフォルトではDate型をサポートしないので、DateをRawRepresentableに準拠させる必要があります。@AppStorage with Date in SwiftUI

swift

1struct CalendarApealView: View { 2 @AppStorage("selectedDate") private var date: Date = Date() 3 // @State private var date: Date = Date() 4 ... 5} 6 7extension Date: RawRepresentable { 8 public var rawValue: String { 9 self.timeIntervalSinceReferenceDate.description 10 } 11 12 public init?(rawValue: String) { 13 self = Date(timeIntervalSinceReferenceDate: Double(rawValue) ?? 0.0) 14 } 15}

投稿2022/08/06 05:55

yodo

総合スコア28

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

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

yodo

2022/08/06 07:10 編集

あと関係ないですが、confirmボタンのアクションの dateFormatter.date(from: dateValue) これは常にnilとなる気がします、、 "12"といった値を取るdateValueを"yyyy/MM/dd"のフォーマットに当てはめることはできないので
guest

0

ベストアンサー

選択した日付、selectedDateを保存したいのですが、すでに
@Binding var selectedDate: Date
と定義しているため、@AppStorageを追加する訳にもいかず、困っているという状況です。

Use a binding to create a two-way connection between a property that stores data, and a view that displays and changes the data. A binding connects a property to a source of truth stored elsewhere, instead of storing data directly. For example, a button that toggles between play and pause can create a binding to a property of its parent view using the Binding property wrapper.
(機械翻訳)データを保存するプロパティと、そのデータを表示・変更するビューの間に双方向の接続を作成するには、バインディングを使用します。バインディングは、データを直接格納する代わりに、他の場所に格納された真実のソースにプロパティを接続します。例えば、再生と一時停止を切り替えるボタンは、Bindingプロパティラッパーを使用して、その親ビューのプロパティへのバインディングを作成することができます。
https://developer.apple.com/documentation/swiftui/binding

@Bindingを使っている理由は次の通りになると思いますので、
「データを保存するプロパティと、そのデータを表示・変更するビューの間に双方向の接続作成する」
@AppStorageを使うのは呼び出し元のビューになると思います。
(呼び出し先のビュー内で完結するのであれば@Bindingではなく@Stateなどを使っていると思いますので)

ですが、保存したいプロパティ(date)はDate型で、どうやら@AppStorageはDate型に対応していない?ように見えました。
そのため、@AppStorageを使うとしたら、dateプロパティとは別に、String型などのプロパティを用意して、そのプロパティに対して@AppStorageをつける感じになると思いました。

次のようなイメージはいかがでしょうか。
// *****のコメントのところが修正ポイントです。

swift

1import SwiftUI 2import FSCalendar 3import UIKit 4 5struct CalendarTestView: UIViewRepresentable { 6 @Binding var selectedDate: Date 7 func makeUIView(context: Context) -> UIView { 8 9 typealias UIViewType = FSCalendar 10 11 let fsCalendar = FSCalendar() 12 13 fsCalendar.delegate = context.coordinator 14 fsCalendar.dataSource = context.coordinator 15 16 return fsCalendar 17 } 18 19 func updateUIView(_ uiView: UIView, context: Context) { 20 // ***** コードから日付を選択状態にします。 21 guard let cal = uiView as? FSCalendar else { 22 return 23 } 24 cal.select(selectedDate) 25 } 26 27 func makeCoordinator() -> Coordinator{ 28 return Coordinator(self) 29 } 30 31 class Coordinator: NSObject, FSCalendarDelegate, FSCalendarDataSource { 32 var parent:CalendarTestView 33 34 init(_ parent:CalendarTestView){ 35 self.parent = parent 36 } 37 38 func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { 39 parent.selectedDate = date 40 } 41 42 } 43} 44 45struct CalendarApealView: View{ 46 // **** DateをStringにするためのDateFormatterです。"(yyyy/MM/dd)" 47 let testFormatter: DateFormatter = { 48 let f = DateFormatter() 49 f.dateStyle = .medium 50 f.timeStyle = .none 51 f.locale = Locale(identifier: "ja_JP") 52 return f 53 }() 54 // ***** UserDefaultsのプロパティです。 55 @AppStorage("datekey") private var dateStorage: String = "" 56 @State private var date: Date = Date() 57 @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode> 58 @Binding var day: String 59 60 @State var onSwitch = false 61 @State var text = "hello" 62 var differString: String { 63 64 let calendar = Calendar.current 65 //today1は今日の日付 66 let today1 = Date() 67 let today2 = calendar.dateComponents([.year, .month, .day], from: today1) 68 let today3 = calendar.date(from: DateComponents(year: today2.year, month: today2.month, day: today2.day))! 69 let date2 = calendar.dateComponents([.year, .month, .day], from: date) 70 let date3 = calendar.date(from: DateComponents(year: date2.year, month: date2.month, day: date2.day))! 71 let result = calendar.dateComponents([.day], from: date3, to: today3) 72 return "\(result.day!)" 73 } 74 func set(_: Any?, forKey:String){ 75 76 } 77 var body: some View { 78 HStack{ 79 VStack{ 80 Text("When did you start this food style?") 81 .font(.title2) 82 CalendarTestView(selectedDate: $date) 83 .frame(height: 400) 84 HStack(){ 85 Text(differString) 86 .font(.title) 87 .padding() 88 .foregroundColor(Color(red: 0.324, green: 0.758, blue: 0.49)) 89 Text("Days") 90 .font(.title2) 91 } 92 93 Button(action: { 94 //ここでdayに値代入! 95 day = differString 96 UserDefaults.standard.set(self.differString, forKey: "String") 97 func StringToDate(dateValue: String, format: String) -> Date { 98 let dateFormatter = DateFormatter() 99 dateFormatter.calendar = Calendar(identifier: .gregorian) 100 dateFormatter.dateFormat = format 101 return dateFormatter.date(from: dateValue) ?? Date() 102 } 103 date = StringToDate(dateValue: differString, format: "yyyy/MM/dd") 104 self.onSwitch.toggle() 105 self.presentationMode.wrappedValue.dismiss() 106 }) { 107 Text("confirm") 108 .font(.title2) 109 .padding(16) 110 .background(Color(red: 0.324, green: 0.758, blue: 0.49)) 111 .foregroundColor(Color.white) 112 .cornerRadius(10) 113 } 114 } 115 }.navigationTitle("Day Setting") 116 .onAppear(perform: onAppearAction) // ***** アクションを追加します。 117 .onChange(of: date, perform: onChangeAction) // ***** アクションを追加します。 118 } 119 // ***** UserDefaultsのプロパティからdateプロパティへ設定します。 120 func onAppearAction() { 121 guard !dateStorage.isEmpty, 122 let d = testFormatter.date(from: dateStorage) else { 123 return 124 } 125 date = d 126 } 127 // ***** dateプロパティからUserDefaultsのプロパティへ設定します。 128 func onChangeAction(value: Date) { 129 dateStorage = testFormatter.string(from: value) 130 } 131} 132 133struct CalendarTestView_Previews: PreviewProvider { 134 static var previews: some View { 135 CalendarApealView(day: .constant("12")) 136 } 137}

投稿2022/08/06 05:46

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問