Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do you refresh the grid? #26

Closed
brendand opened this issue Sep 16, 2021 · 11 comments
Closed

How do you refresh the grid? #26

brendand opened this issue Sep 16, 2021 · 11 comments

Comments

@brendand
Copy link

I have a macOS app that I'm adding Grid to, but when I pass in new data, the grid is not refreshed.

However, if I switch to using LazyVGrid, it does refresh. So I know the data is getting to the SwiftUI view. I'm integrating Grid using an NSHostingController and I'm monitoring changes to my object using @ObjectBinding. Anything outside the Grid will update when I change objects, but anything within the Grid does not.

Think of a master/detail view. The master is an AppKit NSTableView that shows a list of records. The detail view is my SwiftUI view that's using Grid to display a list of fields on screen. But when I switch records, the SwiftUI view's Grid never refreshes.

@brendand
Copy link
Author

brendand commented Sep 17, 2021

I tried turning off the cache, but that didn't work.

Grid(tracks: 3) {
// code
}.gridCache(.noCache)

@brendand
Copy link
Author

So is there no way to refresh the grid when new data arrives?

The only way I've been able to make it work is by tearing down my entire NSHostingController, removing the view, then re-creating the NSHostingController and the SwiftUIView that contains the Grid and sending the data into it again.

But it works fine with LazyVGrid, just not with this Grid component. It just always shows the first piece of data that was passed to it.

There must be a better way to refresh the grid when new data is sent to my SwiftUI view.

@brendand
Copy link
Author

So I noticed that the layout cache is for iOS only. I'm working on a Mac app at the moment. So the cache shouldn't affect me.

But why actually is the cache only for the iOS version? Just curious.

I'm still not understanding why I'm not seeing any new data appear on screen when I pass in new data. Yet with LazyV/HGrid, it does work to show new data.

@brendand
Copy link
Author

Ok, so I'm going to post my code that I'm working with. If anyone sees anything wrong with my approach I'd love to know what I'm doing wrong. Basically I have a list of objects I'm iterating over and depending on their types, I'm generating different kinds of views.

When I pass in new data into the ObservableSelection, I do see that the code within the Grid block is getting executed and each of my views within the Switch statement are being called. Except the actual body of each of the individual views is never called (except when first displayed).

Maybe this has something to do with Grid, I'm not sure. All I know is if I change to LazyVGrid, it works. But I don't get the layout I want.

Any ideas?

struct RecordEditSwiftUIView: View {

	let mainContext : NSManagedObjectContext
	let databaseDocument : TFDatabaseDocument
	private var fields : [CDField] = []
	
	@State var selectedField: CDField?

	@State private var dateModified : Date?
	
	@ObservedObject private var selection : ObservableSelection
	
	init(mainContext: NSManagedObjectContext, databaseDocument: TFDatabaseDocument, selection : ObservableSelection) {
		self.selection = selection
		self.mainContext = mainContext
		self.databaseDocument = databaseDocument
		self.fields = selection.form.sortTheFields() ?? []
	}
	
	var body: some View {
		
		VStack {
				
			if let values = selection.record.formattedValues {

				Grid(tracks:3) {

					ForEach(values, id: \.self) { value in

						if let field : CDField = value.keys.first {

							let fieldValue = value[field] ?? ""

							switch field.fieldType {

							case kFieldTypeNumber:
								TextFieldView(record: selection.record, field: field, fieldValue: fieldValue, selectedField: $selectedField)
									.gridItemAlignment(.topLeading)
									.frame(maxHeight:70)

							case kFieldTypePhoto:
								PhotoFieldView(record: selection.record, field: field, selectedField: $selectedField)
									.gridSpan(row: 3)
									.gridItemAlignment(.topLeading)

							case kFieldTypeNote:
								let attributedValue : NSAttributedString? = selection.record.attributedValueFor(field: field)
								NoteFieldView(record: selection.record, field: field, fieldValue: attributedValue ?? NSAttributedString(string: ""), selectedField: $selectedField)
									.gridSpan(column: 3)
									.gridItemAlignment(.topLeading)
									.frame(maxHeight:200)

							case kFieldTypeSection:
								SectionFieldView(record: selection.record, field: field, selectedField: $selectedField)
									.gridSpan(column: 3)
									.gridItemAlignment(.topLeading)
									.frame(maxHeight:40)

							case kFieldTypeWebSite:
								TextFieldView(record: selection.record, field: field, fieldValue: fieldValue, selectedField: $selectedField)
									.gridItemAlignment(.topLeading)
									.gridSpan(column: 2)
									.frame(maxHeight:70)
								
							default:
								TextFieldView(record: selection.record, field: field, fieldValue: fieldValue, selectedField: $selectedField)
									.gridItemAlignment(.topLeading)
									.frame(maxHeight:70)

							}

						}

					}
					
				}.gridContentMode(.scroll)
		
			} else {
				Spacer()
				Text("no selected record")
				Spacer()
			}
			
		}.padding(.top)
			.padding(.leading)
			.padding(.trailing)
			.frame(minWidth: 600, maxWidth:.infinity)
			.onTapGesture {
				selectedField = nil
			}
			.background(Color(NSColor.controlBackgroundColor))
	}
}

@brendand
Copy link
Author

brendand commented Sep 25, 2021

So I found that if I add .id(arc4random()) to the Grid view, then it does refresh when I pass in new data. But it's slow compared to using LazyVGrid. Quite a bit slower in fact.

Is there a better solution?

@brendand
Copy link
Author

Is this project dead though? Not one response. Hmm...

@f3dm76
Copy link
Collaborator

f3dm76 commented Oct 11, 2021

Hey @brendand, no, the project isn't dead, but unfortunately we do not have time right now to address this issue. But rest assured we will most certainly address this once we find time. Have a nice day

@brendand
Copy link
Author

Hi @f3dm76 Thanks so much for your response. That's good to know. Using the random .id does work, but it's slow, so hoping there's a better solution possible. Looking forward to seeing what you are eventually able to come up with. Thanks for creating this project. It's really great!

@brendand
Copy link
Author

I'm going to close this issue because as I was working on other refresh problems with my SwiftUI layout (non Grid related), I learned more about Swift and found a better way of gathering the objects that I wanted to render from my Core Data object store. I had almost given up on Grid and was using LazyVGrid, but decided to give Grid another chance and low and behold, the refresh was working now without having to generate a random .id for the grid. And it's refreshing just as fast as LazyVGrid now too.

Previously I was just looping through an array of Core Data objects directly, then calling a custom bind function to get and set the values. Now I build a Swift array of structs out of those Core Data objects and iterate over those. That seems to have done the trick.

@medef00
Copy link

medef00 commented Sep 7, 2022

Hi @brendand have you by any chance come across the removal of items from LazyVGrid?

@brendand
Copy link
Author

Hi @Medef I haven't done anything with this in a while actually. Still using Grid, but haven't actually looked at in in a while. I'll get back to it eventually once I'm finished with some updates to my underlying data model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants