Post

Replies

Boosts

Views

Activity

Reply to iOS DisclosureGroup content clipping
Thank you! That solved the problem. I put the ScrollView inside the innermost layer and set the height of the DisclosureGroup based on geometry.size.height. Now it shows and I have a feeling it'll look better on smaller devices. One thing, though. After trying the ScrollView on the outermost view (outside the top-level View), tapping the DisclosureGroup no longer expands it. It won't impact my design, but it might bite someone else. Thanks again!
Jul ’24
Reply to SwiftUI, REST, and websockets
Thanks to @darkpaw for the suggestion. Tried it, but got: "Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update." That was new to me, but it led to more research which led to finding a solution. Here's what ended up working: Changed the ViewModel so the dataIsLoaded attribute, instead of @State is now @Published: class ViewModel: ObservableObject { @Published var appData = CurrentREST() // Codables from JSON @Published var dataIsLoaded : Bool = false ... Changed the invocation of the subview in the main view so it looks like this now: SummaryView(viewModel: viewModel, dataIsLoaded: $viewModel.dataIsLoaded) Note that viewModel.dataIsLoaded is now $viewModel.dataIsLoaded (the $ is in front). Then inside the fetchData function, once the download and parsing are completed, toggling the dataIsLoaded value triggers the next step: func fetchData() async { await _ = WebService().downloadData(fromURL: "current") { currentData in DispatchQueue.main.async { self.appData = currentData self.dataIsLoaded = true } } } Lastly, inside the SummaryView subview, this required changing the @State to @Binding, as was suggested. However... this also led to another discovery that the whole thing could be simplified. I can completely do away with the whole secondary dataIsLoaded flag in the main view and passed down to the subview and just drive the whole thing from the ViewModel. class ViewModel: ObservableObject { @Published var appData = CurrentREST() @Published var dataIsLoaded : Bool = false // <-- important func fetchData() async { await _ = WebService().downloadData(fromURL: "current") { currentUIData in DispatchQueue.main.async { self.appData = currentUIData self.dataIsLoaded = true // This triggers the update } } } } Then inside the main view, you just pass the ViewModel down to the subview, so it can use the REST data as it sees fit: SummaryView(viewModel: viewModel) In the subview, the View is declared so its onChange handler catches the update in the ViewModel's dataIsLoaded field: struct SummaryView: View { @ObservedObject var viewModel: ViewModel ... var body: some View { ... }.onChange(of: viewModel.dataIsLoaded) { setupWebSocket() } This is much simpler! I'm leaving details on both approaches here for anyone else who might be having the same problem. The nice part is you can have multiple subviews that need the same ViewModel data and they can all use the same technique to update themselves once the REST data is loaded and parsed. The only part I don't like is the implicit triggering of the subview hierarchy update, which will make it hard for someone else to come around and follow the control flow. But with sufficient documentation, hopefully, that can be mitigated. Thanks again to @darkpaw for setting me off on a different breadcrumb trail.
Jun ’24