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

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

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

Q&A

解決済

1回答

753閲覧

配列のソートと日を跨いだ表示

uAd30HzDXwVQVwb

総合スコア4

0グッド

0クリップ

投稿2022/06/09 10:07

編集2022/06/09 19:19

下記のコードについて2点ほど質問があります。
現在出来ることは、予定の追加,編集,削除,予定のある日に印付ける(なぜか一度画面を消さないと、印が付かないです。),iOSカレンダーへの追加です。
文字数の関係で切った部分があり分かりにくくてすみません。

①開始時間の早い予定を前にしたいのです。現在は予定を書いた順に表示されます。

②日を跨ぐ開始時間と終了時間を設定した場合、表示された予定がその通りに表示されるようにしたいです。自分で考えると削除や編集の時に通用しなくなってしまいます。

2つの質問、そして長いコード失礼します。お力をお貸しいただければと思います。
よろしくお願いします。

Swift

1import SwiftUI 2import FSCalendar 3import CalculateCalendarLogic 4import EventKit 5 6struct CalendarPage:View{ 7 @State var show:Bool=false 8 @State var show2:Bool=false 9 @State var isSheet:Bool=false 10 @State var selectedDate=Date() 11 @State var array:[Date] 12 @State var n=0 13 @State var m1=Date() 14 @State var m2=Date() 15 @State var sortarrays:[Sortarray] 16 var body: some View{ 17 ScrollView{ 18 Calendarpage(selectedDate:$selectedDate,array:$array) 19 Text(selectedDate,style:.date).padding() 20 Button(action:{self.show=true;m1=selectedDate;m2=selectedDate}){ Text("+")}.sheet(isPresented:self.$show){SecondView(show:$show,selectedDate:$selectedDate,array:$arraynewsta,rt:$m1,newfinish:$m2,sortarrays:$sortarrays)} 21 if(array.count==0){ 22 } 23 else{ 24 ForEach(0..<array.count, id:\.self){index in 25 if(equals(array[index], selectedDate)){ 26 Text("\(sortarrays[index].arraystart,style:.time)\(sortarrays[index].arrayfinish,style:.time)") 27 Button(action: {isSheet=true}) {Text("削除")}.actionSheet(isPresented: $isSheet) { ActionSheet(title: Text("この予定を削除しますか?"), buttons: [.destructive(Text("削除"), action:{remove(index)}),.cancel(Text("キャンセル"), action: {})])} 28 Button(action:{self.show2 = true;n=index}){Text("編集")}.sheet(isPresented:self.$show2){HenshuView(show2:$show2,selectedDate:$selectedDate,array:$array,index:$n,newtitle:$arraytitle[index],newmemo:$arraymemo[index],newstart:$arraystart[index],newfinish:$arrayfinish[index],sortarrays:$sortarrays)} 29 Text("\(sortarrays[index].arraytitle)") 30 Text("\(sortarrays[index].arraymemo)") 31 } 32 } 33 } 34 } 35 } 36 init(){ 37 UIDatePicker.appearance().minuteInterval=5 38 let a=UserDefaults.standard.array(forKey:"key") as? [Date] ?? [] 39 let sort=UserDefaults.standard.array(forKey:"sortkey") as? [Sortarray] ?? [] 40 _array=State(initialValue:a) 41 _sortarrays=State(initialValue:sort) 42 } 43 func equals(_ date1:Date, _ date2:Date)->Bool{ 44 let calendar = Calendar.current 45 let components1=calendar.dateComponents([.year,.month,.day],from:date1) 46 let components2=calendar.dateComponents([.year,.month,.day],from:date2) 47 let result = components1 == components2 48 return result 49 } 50 func remove(_ n:Int){ 51 array.remove(at:n); 52 sortarrays.remove(at:n) 53 UserDefaults.standard.set(array,forKey:"key") 54 UserDefaults.standard.set(array,forKey:"sortkey") 55 } 56} 57struct Sortarray:Codable{ 58 var arraytitle:String 59 var arraymemo:String 60 var arraystart:Date 61 var arrayfinish:Date 62} 63struct SecondView: View{ 64 @Binding var show:Bool 65 @Binding var selectedDate:Date 66 @Binding var array:[Date] 67 @Binding var newstart:Date 68 @Binding var newfinish:Date 69 @Binding var sortarrays:[Sortarray] 70 @State var newtitle="" 71 @State var newmemo="" 72 let eventStore=EKEventStore() 73 var body: some View { 74 VStack { 75       Button(action:{addEvent();save();self.show.toggle()}){Text("保存")} 76 TextField("タイトル",text:$newtitle) 77 Text("メモ") 78 TextEditor(text:$newmemo) 79 DatePicker("開始時間",selection:$newstart) 80 DatePicker("終了時間",selection:$newfinish) 81 } 82 .onAppear(perform:self.allowAuthorization) 83 } 84 func allowAuthorization(){ 85 if getAuthorization_status(){ 86 return 87 }else{ 88 eventStore.requestAccess(to:.event,completion:{(granted, error) in 89 if granted{ 90 return 91 } 92 }) 93 } 94 } 95 func getAuthorization_status()->Bool{ 96 let status=EKEventStore.authorizationStatus(for:.event) 97 switch status { 98 case .notDetermined: 99 print("NotDetermined") 100 return false 101 case .denied: 102 print("Denied") 103 return false 104 case .authorized: 105 print("Authorized") 106 return true 107 case .restricted: 108 print("Restricted") 109 return false 110 @unknown default: 111 literalExpression() 112 fatalError("カレンダーの認証部分でエラー @unknown default") 113 } 114 } 115 func addEvent(){ 116 let defaultCalendar=eventStore.defaultCalendarForNewEvents 117 let event=EKEvent(eventStore:eventStore) 118 event.title=newtitle 119 event.notes=newmemo 120 event.startDate=newstart 121 event.endDate=newfinish 122 event.calendar=defaultCalendar 123 do{try eventStore.save(event, span:.thisEvent) 124 } catch let error{print(error)} 125 } 126 func literalExpression(){ 127 print("__FILE__",#file) 128 print("__LINE__",#line) 129 print("__COLUMN__",#column) 130 print("__FUNCTION__",#function) 131 } 132 func save(){ 133 let encoder=JSONEncoder() 134 let ej=Sortarray(arraytitle:newtitle,arraymemo:newmemo,arraystart:newstart,arrayfinish:newfinish) 135 if let encodedValue=try? encoder.encode(ej){ 136 sortarrays.append(ej) 137 UserDefaults.standard.set(encodedValue,forKey:"sortkey") 138 } 139 array.append(selectedDate); 140 UserDefaults.standard.set(array,forKey:"key") 141 } 142} 143struct HenshuView: View{ 144 @Binding var show2:Bool 145 @Binding var selectedDate:Date 146 @Binding var array:[Date] 147 @Binding var index:Int 148 @Binding var newtitle:String 149 @Binding var newmemo:String 150 @Binding var newstart:Date 151 @Binding var newfinish:Date 152 @Binding var sortarrays:[Sortarray] 153 let eventStore=EKEventStore() 154 var body: some View{ 155 Button(action:{addEvent();saveremove(index);self.show2.toggle()}){Text("保存")} 156 TextField("タイトル",text:$newtitle) 157 Text("メモ") 158 TextEditor(text:$newmemo) 159 DatePicker("開始時間",selection:$newstart) 160 DatePicker("終了時間",selection:$newfinish) 161 } 162 func saveremove(){ 163 let encoder=JSONEncoder() 164 let ej=Sortarray(arraytitle:newtitle,arraymemo:newmemo,arraystart:newstart,arrayfinish:newfinish) 165 if let encodedValue=try? encoder.encode(ej){ 166 sortarrays.append(ej) 167 UserDefaults.standard.set(encodedValue,forKey:"sortkey") 168 } 169 array.append(selectedDate); 170 array.remove(at:n); 171 sortarrays.remove(at:n) 172 UserDefaults.standard.set(array,forKey:"key") 173 UserDefaults.standard.set(array,forKey:"sortkey") 174 } 175 func addEvent(){ 176 let defaultCalendar=eventStore.defaultCalendarForNewEvents 177 let event=EKEvent(eventStore: eventStore) 178 event.title=newtitle 179 event.notes=newmemo 180 event.startDate=newstart 181 event.endDate=newfinish 182 event.calendar=defaultCalendar 183 do{try eventStore.save(event,span:.thisEvent) 184 }catch let error{print(error)} 185 } 186} 187struct Calendarpage: UIViewRepresentable{ 188 @Binding var selectedDate:Date 189 @Binding var array:[Date] 190 func makeUIView(context:Context)->UIView{ 191 typealias UIViewType=FSCalendar 192 let fsCalendar=FSCalendar() 193 fsCalendar.delegate=context.coordinator 194 fsCalendar.dataSource=context.coordinator 195 return fsCalendar 196 } 197 func updateUIView(_ uiView:UIView,context:Context){ 198 } 199 func makeCoordinator()->Coordinator{ 200 return Coordinator(self,array:$array) 201 } 202 class Coordinator: NSObject,FSCalendarDelegateAppearance, FSCalendarDataSource{ 203 @Binding var array:[Date] 204 var parent:Calendarpage 205 init(_ parent:Calendarpage,array:Binding<[Date]>){ 206 self.parent=parent 207 self._array=array 208 } 209 func calendar(_ calendar:FSCalendar,didSelect date:Date,at monthPosition:FSCalendarMonthPosition){ 210 parent.selectedDate=date 211 } 212 func calendar(_ calendar:FSCalendar,numberOfEventsFor date:Date)->Int{ 213 if array.contains(date){ 214 return 1 215 } else { 216 return 0 217 } 218 } 219 } 220}

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2022/06/09 14:05

数日の間に随分コードが進化しましたね。 > ①開始時間の早い予定を前にしたいのです。現在は予定を書いた順に表示されます。 DateやStringを個別の配列にして扱うのはちょっと大変になってきたように思います。 structにまとめて、structの配列にした方が扱いやすいと思いました。 それからstructをソートすると良いと思います。 https://developer.apple.com/documentation/swift/array/sort(by:) ②はちょっとまだ自分の理解が追いついていません・・ もうちょっとお待ちください・・
uAd30HzDXwVQVwb

2022/06/09 19:14 編集

ご返答ありがとうございます! 毎回本当にxg63ex2bさんに助けられています。 今回も私の質問に真摯に向き合っていただき感謝しております。 ①への解決方法ありがとうございます。 構造体配列に予定を追加することは出来たようですが、記入後は表示されるものの、一度アプリを閉じると表示がされない事態が起こりました。 解決に時間がかかるかもしれないので、現時点でのコードを質問文で記載します。 上手くいったり変化すれば、また新しく修正します。 面倒くさい問題を投げかけてしまいすみません。 いつまでも待ちますので、気にしないでください! 考えていただけるだけで感謝ですので。
guest

回答1

0

ベストアンサー

コメントありがとうございます。

あらためて回答欄に入力します。

①開始時間の早い予定を前にしたいのです。

DateやStringの個別の配列はまとめてstructの配列にすると良いと思います。
それからstructの配列をソートすると良いと思います。
sort(by:)

一度アプリを閉じると表示がされない事態が起こりました。

質問欄のコードも修正されているのですね。
まだ修正の途中かもしれませんが、JSONにエンコードして保存していたり、読み込むときはそのまま配列のままだったりしますので、このあたりを見直すと良いかもしれませんね。

②日を跨ぐ開始時間と終了時間を設定した場合、

array:[Date]を基準に考えているのですかね。
基準となる日付もstructの中に持ってしまっても良いのではないかと思いました。
structの中には開始日時(日付と時刻)、終了日時(日付と時刻)という形で保持しておいて。
これだけだと0:00の場合に時刻の指定有無が区別がつかないと思いますので、別途時刻指定有無のフラグ的なものも必要かもしれません。
表示する際は「struct.開始日時 <= 選択日付 <= struct.終了日時」のような条件にする感じでしょうか。

ForEachは1つのコレクションを対象にする場合はrangeとindexを使う感じではなく、コレクションの要素を使う感じにすると良いと思いました。

SwiftUI Essentials Building Lists and Navigation > Section 5 Make the List Dynamic > Step 2
https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation

https://developer.apple.com/documentation/swiftui/foreach/init(_:content:)-6oy5i


********** 追記です。 **********

コメントありがとうございます。
連日遅くまで頑張りましたね。
結構根本的なところからの変更になりそうでしたが、1日でできてしまったのですね、すごいです。

再描画することは難しいのでしょうか。

最小限のコードで再現確認しました。

FSCalendar.reloadData()で再読み込みしてくれるみたいですので、
updateUIView(_:context:)の中でarrayを最新にしてあげてから呼び出してあげると良いと思います。

https://github.com/WenchaoD/FSCalendar/blob/c4a443ac79bd62e483093aa3916aaa7347f1645e/MOREUSAGE.md#qcan-we-refresh-the-calendar-after-a-network-request

@BindginはViewプロトコルに準拠した構造体の中に記述するのが一般的と思いました。
SwiftUIのチュートリアルも見てみると良いかもしれません。

Section 6 Create a Favorite Button for Each Landmark > Step 2
https://developer.apple.com/tutorials/swiftui/handling-user-input

swift

1import SwiftUI 2import FSCalendar 3 4struct CalendarPage:View{ 5 @State var selectedDate=Date() 6 @State var array=[Date]() 7 var body: some View{ 8 Calendarpage(selectedDate:$selectedDate,array:$array) 9 Button("+", action: action) 10 } 11 func action() { 12 let calendar = Calendar.current 13 let date1 = Date() 14 guard let date2 = calendar.date(byAdding: .day, value: array.count + 1, to: date1) else { 15 return 16 } 17 let components1 = calendar.dateComponents([.year, .month, .day], from: date2) 18 guard let date3 = calendar.date(from: components1) else { 19 return 20 } 21 array.append(date3) 22 } 23} 24struct Calendarpage: UIViewRepresentable{ 25 @Binding var selectedDate:Date 26 @Binding var array:[Date] 27 func makeUIView(context:Context)->UIView{ 28 typealias UIViewType=FSCalendar 29 let fsCalendar=FSCalendar() 30 fsCalendar.delegate=context.coordinator 31 fsCalendar.dataSource=context.coordinator 32 return fsCalendar 33 } 34 func updateUIView(_ uiView:UIView,context:Context){ 35 // ***** FSCalendar.reloadDataで再読み込みしてくれるみたいです 36 guard let v = uiView as? FSCalendar else { 37 return 38 } 39 context.coordinator.array = array 40 v.reloadData() 41 } 42 func makeCoordinator()->Coordinator{ 43 return Coordinator(self,array:array) 44 } 45 class Coordinator: NSObject,FSCalendarDelegateAppearance,FSCalendarDataSource{ 46 // ***** arrayは @Binding から通常のプロパティにしました 47 var array:[Date] 48 var parent:Calendarpage 49 init(_ parent:Calendarpage,array:[Date]){ 50 self.parent=parent 51 self.array=array 52 } 53 func calendar(_ calendar:FSCalendar,didSelect date:Date,at monthPosition:FSCalendarMonthPosition){ 54 parent.selectedDate=date 55 } 56 func calendar(_ calendar:FSCalendar,numberOfEventsFor date:Date)->Int{ 57 if array.contains(date){ 58 return 1 59 } else { 60 return 0 61 } 62 } 63 } 64}

投稿2022/06/10 00:48

編集2022/06/11 00:29
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

uAd30HzDXwVQVwb

2022/06/10 19:59

ご回答誠にありがとうございます! ご連絡遅くなりすみません。 ご回答を参考に1日程かけて①も②も出来るようになりました。 今回も丁寧な対応感謝致します。 申し訳ないのですが、もう一つ質問してもよろしいでしょうか。 予定を入力するとその日にポイントが付くようにしています。 ですが現在予定入力直後にはポイントが付かず、一度アプリを閉じて再度開くとポイントが付いています。 Calendarpage(selectedDate:$selectedDate,array:$array)をアプリ起動時に一度だけ呼んでいて、予定の追加後に再描画していないことが原因だと考えています。 いろいろ試しましたが、どれも上手くいきませんでした。 再描画することは難しいのでしょうか。
uAd30HzDXwVQVwb

2022/06/11 05:10

お褒めの言葉有り難く思います。 ご連絡遅くならないようにと頑張りました。 追記ありがとうございます! reloadDataというのがあるのですね。 活用させていただきます。 長々と面倒くさい質問に答えていただき本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問