Post

Replies

Boosts

Views

Activity

Is there a way to hide NavigationView inline title until user scrolls past a certain point
like the title suggests, I'm trying to collapse the navigationView title bar until the user scrolls past the first section (which I'm using as a header). Please see image.  The reason why I can't use SwiftUI's built in NavigationTitle is because I can't put the profile picture in the NavigationTitle. Anyways, once the word "Summary" disappears, I want the navigationView's inline title to appear and say "Summary". I've tried using .onDisappear and .onAppear on the Text("Summary"), but because of the way the navigation bar works, it doesn't trigger until the user scrolls pretty far down the page. I want a solution that: 1] collapses the top bar until the word "Summary" is out of view, then 2] shows the inline title. Thank you! I have a @State var that changes the navigation title: @State var navigationTitle: String = "" And a NavigationTitle that changes: .navigationBarTitle("\(navigationTitle)", displayMode: .inline) And here is my header view: Section { HStack(spacing: 8) { VStack(alignment: .leading, spacing: 5) { Text("\(Date().formatted(date: .abbreviated, time: .omitted))")                 .foregroundColor(Color.secondary) .textCase(.uppercase) .font(.system(size: 13, weight: .medium, design: .default))                     .onAppear {                     navigationTitle = "" } .onDisappear { navigationTitle = "Summary" } Text("Summary") .font(.largeTitle.bold()) .textCase(nil) .foregroundColor(Color.primary) } Spacer() Button { self.showUserProfileModal.toggle() } label: { Image(systemName: "person.crop.circle") .symbolRenderingMode(.palette) .foregroundStyle(.blue, .blue, .white) .background(Color.white) .clipShape(Circle()) .font(.system(size: 37, weight: .light, design: .default))        } } .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))                     .listRowBackground(Color(.systemGroupedBackground)) }
1
0
1.5k
Jan ’22
Approach for creating bar chart from data
Hello, My app allows users to input each drink they have to track water consumption. These drinks are saved in CoreData. Each entry has the quantity of the drink and the time it was consumed. Now, I want to create a bar graph that shows how much drink was consumed each hour in the current day. I want it to look exactly like the bar graph in the Apple Fitness app (attached), with 24 vertical lines representing each hour of the day of different height depending on how much drink was consumed in the given hour. The issue is, I need to first fetch all the drinks the user consumed in the current day, then separate them into the different hours. I have no idea I should approach this. Out of desperation, I created 24 fetch requests for the different hours and now have 24 values of quantity (one for each hour). But this is extremely tedious and would just get near impossible to manage. The most efficient way to create a bar chart is to have a for each loop and draw lines for each hour, but I don't know how to get the data in a way that would let me do this. Any guidance is appreciated! Thank you!
1
0
476
Jan ’22
CoreData FetchRequest doesn't update view
I have an app that lets users log drinks. Users create CoreData entries that have attributes like drink type and quantity. I have a view that lets users the quantity they have consumed in the current day. This is done using a FetchRequest and a calendar predicate that only fetches entries from the current day. I am using @FetchRequest property wrapper in the view, then initializing it with init() {}. Inside the initializer, I set the predicate, sort descriptors, and use self._todayIntakeEvents ... to set the value for the fetch request variable. Then, each time the view appears, I use .onAppear to sum the quantity of the fetched items by using todayIntakeEvents.map, then using .reduce to sum the array. The problem: when users add drinks with Siri, the drink information doesn't show up when opening the app from background. However, it will appear if I quit and relaunch the app. What I want to happen is for the drink to appear immediately when the user opens the app again. My code for the view: struct TodaySummaryView: View { @Environment(\.managedObjectContext) private var viewContext     @FetchRequest var todayIntakeEvents: FetchedResults<WaterIntakeEvent> init() {         let calendar = Calendar.current         let dateFrom = calendar.startOfDay(for: Date())         let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)         let predicateTodayEvents = NSPredicate(format: "timeOfConsumption <= %@ AND timeOfConsumption >= %@", dateTo! as CVarArg, dateFrom as CVarArg)         self._todayIntakeEvents = FetchRequest(             entity: WaterIntakeEvent.entity(),             sortDescriptors: [NSSortDescriptor(keyPath: \WaterIntakeEvent.timeOfConsumption, ascending: false)],             predicate: predicateTodayEvents)     } var body: some View { // some UI .onAppear { totalWaterQuantityToday = todayIntakeEvents.map {Int($0.waterQuantity)}             sumOfWaterQuantityToday = totalWaterQuantityToday.reduce(0, +) print(sumOfWaterQuantityToday) // even after Siri creates new CoreData entries, this still prints the quantity from before it added } My code for the Siri Intent Handler: public func handle(intent: LogDrinkIntent, completion: @escaping (LogDrinkIntentResponse) -> Swift.Void) {         let context = PersistenceController.shared.container.viewContext         let newDrink = WaterIntakeEvent(context: context)         newDrink.drinkType = intent.drinkType         newDrink.quantity = intent.quantity as! Float         newDrink.waterQuantity = intent.quantity as! Float         newDrink.timeOfConsumption = Date()         newDrink.id = UUID()         do {             try context.save()             let response = LogDrinkIntentResponse(code: LogDrinkIntentResponseCode.success, userActivity: nil)             completion(response)             print("Successfully saved.")         } catch {             completion(LogDrinkIntentResponse(code: LogDrinkIntentResponseCode.failure, userActivity: nil))             print("Error saving.")         }     } I suspect the problem is not Siri (because the data appears after relaunching the app) but that because I initialized the view with the FetchRequest, it's not responding to changes. Can someone help please?
1
0
1k
Jan ’22
Organize CoreData items for use in bar chart
Hello, I am building an app where users can log their water consumption. I want to create a bar chart which displays how much water they are drinking each hour in the current day (from 12 a.m. to 11:59 p.m.). I have a CoreData entity which stores the quantity of water consumed and the time of consumption. The chart will be hours on the x-axis and ounces on the y-axis. To get the desired bar chart, I plan on making a ForEach loop to create 24 vertical lines to represent the water intake each hour, then adjust the height of those lines based on the quantity. In order to do this, I need an array of 24 items, each of which stores the quantity of water and the hour of the day. Something like this: struct waterIntake: Identifiable { var id = UUID() var quantity: CGFloat var hour: Int } My question is: how do I get from a CoreData entity with the quantity of water and time of consumption to this array with hour numbers? Or, can I use the CoreData entity directly in the ForEach loop? Please remember I need some way to add the quantity of all water consumed in a given hour and group it into one quantity per one hour.
0
0
396
Jan ’22
Schedule start of notification requests
Hello all, I have an app that allows users to create a repeating event. Based on user-provided start date and repeat pattern (daily, weekly, monthly, etc.), the app schedules a repeating notification using UNUserNotificationCenter with a UNCalendarNotificationTrigger. If the repeat pattern is daily, the user receives notifications each day at the specified time. This works perfectly. But because UNCalendarNotificationTrigger uses a start date and matches components (in the case of daily repetition, it matches the timezone, hour, minute, and second), if the user enters a start date that is in the future, UNCalenderNotificationTrigger fires before the selected start date because all it does is match components. Does anyone have ideas for a solution to how I could fix this issue? I was thinking maybe scheduling the scheduling of the notification for a day in advance of the event. I don't want to use a for loop to create multiple notifications because that it difficult to manage.
12
0
3.0k
Feb ’21
Adding Trailing Item to NavigationView Changes Appearance
Hello, I'm relatively new to coding, and I was creating a list under a NavView which I plan on changing into a dynamic list soon. Anyways, my NavView is set up with a title, and I wanted to add a "+" button on the top right, so I used the trailing items to add the button. However, for some reason, the background colors of the list items change and the line dividers between each list item seem to be misaligned. I wanted to know why adding a trailing item messes everything up. Please see the attached screenshots (before trailing item and after trailing item is added) as well as my code. Thank you! struct GradesDisplayRow: View {   var body: some View {     NavigationView {     List {       HStack {         VStack(alignment: .leading) {         Text("32.5 / 33")           .font(.largeTitle)           .padding(.top, 5)           .frame(width: 250.0, alignment: .leading)           Text("Summative Assessment (Chapter 3-5) ").font(.body)             .padding(.bottom, 5)             .frame(width: 280.0, alignment: .leading)         }         Spacer()         Button(action: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Action@*/{}/*@END_MENU_TOKEN@*/) {           Text("EDIT")             .fontWeight(.semibold)             .foregroundColor(Color.darkGray)         }                   .frame(height: 2.0)         .foregroundColor(.black)           .padding()         .background(Color.lightGray)           .cornerRadius(20)             }       HStack {         VStack(alignment: .leading) {         Text("15 / 16.5")           .font(.largeTitle)           .padding(.top, 5)           .frame(width: 250.0, alignment: .leading)           Text("Chapter Review Packet ").font(.body)             .padding(.bottom, 5)             .frame(width: 280.0, alignment: .leading)         }         Spacer()         Button(action: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Action@*/{}/*@END_MENU_TOKEN@*/) {           Text("EDIT")             .fontWeight(.semibold)             .foregroundColor(Color.darkGray)         }                   .frame(height: 2.0)         .foregroundColor(.black)           .padding()         .background(Color.lightGray)           .cornerRadius(20)             }       HStack {         VStack(alignment: .leading) {         Text("3 / 3")           .font(.largeTitle)           .padding(.top, 5)           .frame(width: 250.0, alignment: .leading)           Text("Homework for 2/29 ").font(.body)             .padding(.bottom, 5)             .frame(width: 280.0, alignment: .leading)         }         Spacer()         Button(action: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Action@*/{}/*@END_MENU_TOKEN@*/) {           Text("EDIT")             .fontWeight(.semibold)             .foregroundColor(Color.darkGray)         }                   .frame(height: 2.0)         .foregroundColor(.black)           .padding()         .background(Color.lightGray)           .cornerRadius(20)             }       HStack {         VStack(alignment: .leading) {         Text("32.5 / 33")           .font(.largeTitle)           .padding(.top, 5)           .frame(width: 250.0, alignment: .leading)           Text("Summative Assessment over Triangles ").font(.body)             .padding(.bottom, 5)             .frame(width: 280.0, alignment: .leading)         }         Spacer()         Button(action: /*@START_MENU_TOKEN@*//*@PLACEHOLDER=Action@*/{}/*@END_MENU_TOKEN@*/) {           Text("EDIT")             .fontWeight(.semibold)             .foregroundColor(Color.darkGray)         }                   .frame(height: 2.0)         .foregroundColor(.black)           .padding()         .background(Color.lightGray)           .cornerRadius(20)             }     }.navigationBarTitle("Grades")       .navigationBarItems(trailing: Button(action: {}) {         Image(systemName: "plus")           }       )     .navigationViewStyle(StackNavigationViewStyle())                   }                          } } With Trailing Item - https://developer.apple.com/forums/content/attachment/a4e21c59-89c7-4481-8344-c592d1615316 Without Trailing Item - https://developer.apple.com/forums/content/attachment/091cd90d-e875-45d8-85a4-31a5b13d4750
2
0
756
Jan ’21
Button not dismissing Onboarding Modal
Hello all, My app has a welcome screen that only displays when the app is first launched. I'm doing this using @AppStorage property wrapper. It is set to true initially, which triggers the .sheet(isPresented:) modal view. In the modal view, there is a button that users can press to dismiss the modal view. This button sets a binding variable to false, which in turn should change the AppStorage variable to false. Problem is, the button, when pressed, doesn't dismiss the modal. Please see code below: This is the modal view: struct IntroductionView: View {     @Binding var isShowingIntroScreen: Bool     var body: some View {             VStack {                 Spacer()                     .frame(height: 20)                 ScrollView {                 TitleView()                 Spacer()                 InformationContainerView()                 Spacer()                 }                     Button(action: {                         self.isShowingIntroScreen = false                         print(123)                         }                     ) {                         Text("Get started")                             .customButton()                     }                 .padding()                 Spacer()                     .frame(height: 0)             }     } } This is the content view: struct ContentView: View { @AppStorage("Onboarding View") var isShowingOnboardingScreen = true var body: some View { .background(EmptyView()                 .sheet(isPresented: $isShowingOnboardingScreen, content: { IntroductionView(isShowingIntroScreen: $isShowingOnboardingScreen)                   })           ) When I press the button in the modal view, it prints 123 but doesn't dismiss the modal. Am I doing something wrong?
8
0
947
Feb ’21
Best practice for storing repeating dates in CoreData
Hello everyone, I have an app that lets users create and save meeting information. They can enter a date and set a repeat (like Apple's calendar app) if the meeting repeats every week, every month, every day, etc. I'm storing these dates in CoreData using a date attribute. My questions are: Can I store an array of dates in one date attribute in CoreData? How should I create a repeating rule for the dates? Right now, I let users choose a repeat by using a picker: Picker("Repeats", selection: $repeats) {        Text("Every week").tag("1")        Text("Every month").tag("2")        Text("Every day").tag("3") } I'm thinking this will probably involve a few if statements (e.g. if repeats == 1, set this rule, else if repeats == 2, set this rule, etc.) But what would this rule look like?
7
0
1.5k
Feb ’21
Best practice for removePendingNotificationRequests with CoreData
Hello all, I have a question regarding how to use the removePendingNotificationRequests - https://developer.apple.com/documentation/usernotifications/unusernotificationcenter/1649517-removependingnotificationrequest when implemented in CoreData. Currently, I have an app that lets users save meeting URLs. Each meeting the user creates is stored as a CoreData entity. They can set a time for these meetings, and the app will schedule a local notification for a few minutes before the meeting. The way I'm doing this is passing the meeting time the user selected into a sendNotification function and using that time as a trigger (UNCalendarNotificationTrigger). Problem is -- once a user creates a meeting and the notification is set, the notification will happen even if the user deletes the meeting before the scheduled time. I want to use removePendingNotificationRequests to solve this problem. My question is: removePendingNotificationRequests accepts a string as the identifier for which notification request to remove. But, how do I pass this string to the request? And which string should I use? The meeting's name? I've been thinking about creating a new UUID attribute for my meeting entity so I'll always have a unique string, and passing that UUID to the onDelete function of the meetings. Here's how I'm passing the meeting time to the notification function (this is inside a "Create" button in a AddNewMeeting view): self.notificationManager.sendNotification(title: "Upcoming Meeting", subtitle: nil, body: "\(meetingName) will start in 5 minutes. Tap here to get meeting information.", time: meetingTime, meetingName: "\(meetingName)") This is my current code for the list of meetings: ForEach(items, id: \.self) { item in        OneTimeRow(meeting: item) } .onDelete(perform: deleteItems) And this is the delete function:     private func deleteItems(offsets: IndexSet) {         withAnimation {             offsets.map { items[$0] }.forEach(viewContext.delete)             do {                 try viewContext.save()             } catch {                 print("There was an error deleting items")             }         } removePendingNotificationRequests(withIdentifiers identifiers: [String]) // -- Is this how to put the function in?     } And this is the code for scheduling notifications:     func sendNotification(title: String, subtitle: String?, body: String, time: Date, meetingName: String) {             let content = UNMutableNotificationContent()             content.title = title             if let subtitle = subtitle {                 content.subtitle = subtitle             }             content.body = body             let triggerDate = time - 5 * 60                 let trigger = UNCalendarNotificationTrigger(                             dateMatching: Calendar.current.dateComponents([.timeZone, .year, .month, .day, .hour, .minute], from: triggerDate),                             repeats: true                 )         let request = UNNotificationRequest(identifier: "\(meetingName)", content: content, trigger: trigger)                 UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)         } Thank you!
17
0
2.1k
Feb ’21
Can't Figure Out How to Use Function
Hello everyone, I'm trying to use the func isDateInToday(_ date: Date) - Bool function in my code. I've put it where functions usually go, but it expects me to return something. Basically, I don't know what to put func isDateInToday(_ date: Date) - Bool { // here } I might be really confusing how this function should be used. By the way, I'm calling this function like this: if isDateInToday(oneTimeDate) {     Text("Today at \(oneTimeDate, formatter: itemFormatter)") }
6
0
1.1k
Feb ’21
What type of data for UIActivityViewController?
Hello all, I have an AudioRecorder class where users can record and playback audio using the AVFoundation framework. I'm trying to give users the option to share the m4a audio recording using UIActivityViewController. This share screen needs some data to be shared, in this form: let data = // some data I understand that you can put strings and URL's in this spot, but I'm confused as how to put the user's audio recording in this place. Here is the code for the audio recorder, as well as how the audio file is saved. I'm pretty new to the AVFoundation framework and working with files. class AudioRecorder: NSObject, ObservableObject {     override init() {         super.init()         fetchRecordings()     }     let objectWillChange = PassthroughSubjectAudioRecorder, Never()     var audioRecorder: AVAudioRecorder!     var recordings = [Recording]()     var recording = false {         didSet {             objectWillChange.send(self)         }     }     func startRecording() {             let recordingSession = AVAudioSession.sharedInstance()             do {                 try recordingSession.setCategory(.playAndRecord, mode: .default)                 try recordingSession.setActive(true)             } catch {                 print("Failed to set up recording session")             }             let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]             let audioFilename = documentPath.appendingPathComponent("\(Date().toString(dateFormat: "dd-MM-YY 'at' HH:mm")).m4a")             let settings = [                 AVFormatIDKey: Int(kAudioFormatMPEG4AAC),                 AVSampleRateKey: 12000,                 AVNumberOfChannelsKey: 1,                 AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue             ]             do {                 audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)                 audioRecorder.record()                 recording = true             } catch {                 print("Could not start recording")             }         }     func stopRecording() {             audioRecorder.stop()             recording = false             fetchRecordings()         }     func fetchRecordings() {         recordings.removeAll()         let fileManager = FileManager.default         let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]         let directoryContents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil)         for audio in directoryContents {             let recording = Recording(fileURL: audio, createdAt: getCreationDate(for: audio))             recordings.append(recording)         }         recordings.sort(by: { $0.createdAt.compare($1.createdAt) == .orderedAscending})         objectWillChange.send(self)     }     func getCreationDate(for file: URL) - Date {         if let attributes = try? FileManager.default.attributesOfItem(atPath: file.path) as [FileAttributeKey: Any],             let creationDate = attributes[FileAttributeKey.creationDate] as? Date {             return creationDate         } else {             return Date()         }     }     func deleteRecording(urlsToDelete: [URL]) {         for url in urlsToDelete {             print(url)         do {             try FileManager.default.removeItem(at: url)         } catch {             print("File could not be deleted!")         }     }     fetchRecordings()     } } So ultimately, my question is: what should I put in the let data = part? Thank you so much!
1
0
709
Feb ’21
Using CoreData entity in SwiftUI view
Hello, I'm a little confused as to how to use my attributes that are in an entity in CoreData in a SwiftUI View. In this case, I have an entity called "Meeting", and it has string attributes called "meetingName" and "meetingLink". I have set up a persistence controller and everything seems to work fine, but I've run into an issue where I want to display the meetingName attributes in a list. I'm doing this by creating a view called MeetingRow, then using the following HStack: struct MeetingRow: View {     @Environment(\.managedObjectContext) var managedObjectContext     @FetchRequest(         entity: Meeting.entity(),         sortDescriptors: [             NSSortDescriptor(keyPath: \Meeting.meetingName, ascending: true),             NSSortDescriptor(keyPath: \Meeting.meetingLink, ascending: false)         ]     ) var meetings: FetchedResults<Meeting>     var body: some View {         HStack {             Spacer()             .frame(width: 20)             Text(meetings.meetingName ?? "nil")             } /* This ^^ is the offending line (error: Value of type 'FetchedResults<Meeting>' has no member 'meetingName' */             Spacer()         }     } It tells me that I don't have member meetingName in the fetched results, which is why I'm confused. Thank you in advance for your help!
3
0
1.1k
Feb ’21
Buttons in NavigationView Items Don't Work
Hello, I'm a few weeks into learning SwiftUI, and I'm making an iOS app that allows users to save their online meeting links in a list. I have two buttons in each row of the list, one that lets users join directly from their phone and another that opens a UIActivityViewController to let them AirDrop the link to their Mac. The problem is: whenever I click anywhere on the row (not on the button) it triggers the actions for both buttons. Clicking either of the buttons also triggers both button actions. My code example is below. I made the two buttons print different values for now so it's easier to tell what's going on. NavigationView {             List {                 ForEach(items) { item in                     HStack {                         VStack(alignment: .leading){                             Text("\(item.name ?? "Untitled")")                                 .font(.largeTitle)                                 .padding(.top, 5.0)                             Text("Starts at \(item.time!, formatter: itemFormatter)")                                 .padding(.bottom, 3.0)                         }                         Spacer()                         Button {                             print("Sending to computer")                         } label: {                             Image(systemName: "laptopcomputer.and.iphone")                                 .padding()                         }                         Button {                             print("Joining on iPhone")                         } label: {                             Text("JOIN")                                 .fontWeight(.semibold)                                 .foregroundColor(Color.white)                                 .padding(.trailing, 1.0)                                 .frame(width: 60.0, height: 30.0)                         }                     }                 }                 .onDelete(perform: deleteItems)             }             .listStyle(InsetListStyle())             .navigationBarItems(                 leading: EditButton(),                 trailing:                     Button {                         addItem()                     } label: {                         Image(systemName: "plus")                     })             .navigationTitle("My Meetings")         }     } I want each button click to only register the action of that button, and I don't want the whole row to be clickable. Maybe I shouldn't be using navigation view? I would prefer to keep using navigation view because it lets me add a title and navigationBarItems
1
1
444
Feb ’21
How to Sum User-Generated Data
Hello, I'm trying to find the sum of a user-generated array by using the following code.      let total = pointsReceived.reduce(0, +) The above code doesn't work because, for some reason, it can't find 'pointsReceived' in scope. I have this structure on the top of the page with the following data format: struct Grade: Hashable, Identifiable {   var id = UUID()   var assessmentName: String!   var pointsReceived: Double!   var pointsPossible: Double! } I'm trying to add all the points received (there can be multiple rows of this data). How should I do that? Thank you!
2
0
576
Jan ’21