So here's what I've got:
An NSTableView with an NSMutableArray data source
FSEvents monitoring a folder that contains the file that contains the data for the table view (Using SCEvents for Objective-C abstraction goodness)
The FSEvents triggers the same function that a reload button in the UI does. This function refreshes the table view with a new data source based on the contents of said file via setDataSource:.
And here's what happens:
If I make a change to the file, the FSEvent gets triggered and the refresh method gets called.
The array that the table view should be accepting does indeed include the changes that triggered the FSEvent.
setDataSource: gets sent to the NSTableView with the correct data source.
The changes do not appear in the table view!
But then:
If I hit the refresh button, which triggers the exact same method as the FSEvent, the table view gets updated with the new data.
I also tried replacing the FSEvent with an NSNotification (NSApplicationDidBecomeActiveNotification), but the same thing happens.
Anyone have any idea why this is happening?
Edit: For clarification, the jist of my question is this: Why does my NSTableView reload as it should when triggered by a button press, but not when triggered by an FSEvent or an NSNotification?
Edit: Thanks to diciu, I've figured out that in fact all of my UI references point to 0x0 when triggered by the event, but then have valid addresses when triggered by the button click. These objects are all declared in IB, so there's no instantiation or allocation for them going on in my code. So my question is now: what can I do to stop these pointers from pointing to nil?
We call reloadData on NSTableView when we have new data to add/remove to the table.
This might help, to force the NSTableView to redraw.
I'm not really sure if this is what your asking though. The wording of your question is kind of confusing, you state a series of events, but never a true question.
sounds like when you register for the event/notification, you're passing in a different instance of your controller class.
Have you tried calling your method from your FSEvent on a second pass of the run-loop?
[myObject performSelector:#selector(reloadAction:) withObject:nil afterDelay:0.0];
You're setting an NSArray directly as the data source of the table view?
That's not how NSTableView works. The data source must be an object that conforms to NSTableDataSource. NSArray doesn't. You write the data source yourself; it will probably be the same object that you currently have calling setDataSource:.
The other way would be to use Bindings.
Could it be that reference to table view within the scope of your refresh method is not valid?
I.e. are you sure you're not calling [nil reloadData] which does not yield any errors?
Your reference to your table view might be nil in the refresh code if you're set it before awakeFromNib or in some other circumstances.
Related
Kind of a newbie question, so forgive me if I'm missing some basic concepts...
I have a view controller within a window, which contains multiple NSTableViews, each of which having a variety of associated observers added via addObserver. These work fine and as expected, with notifications going to each of the correct table views. However, if I close the window, then open a new window (of the same type), posted notifications are being sent to the table views of the window that was previously closed.
I was under the impression that as of macOS 10.11 observers did not have to be explicitly removed, which I would assume would happen when the original window is closed... UNLESS I'm missing something fundamental about closing windows. At the moment, I don't do anything special when the user closes a window, and the window just vanished from the screen. Do the views created in my viewDidLoad method live on even after the window is closed? Or no I need to explicitly dispose of these views in a method such as viewWIllDisappear?
Thanks!
Even when object you added as observer is disposed the observer is not. According to official documentation you have to pair each addObserver with corresponding removeObserver. This is true and for NotificationCenter and for KVO. By the way adding object as observer does not increase its owners, so under correct memory management left observer result in run-time crash - that is why needed paired removeObserver (if there is no crash in such situation it means there is leak).
The place where to do this depends on usage. If you add observer in viewWillAppear then it is better to remove it in viewWillDisappear, if you add on creation then remove should be done in deinit
Not even sure if this is feasible, but here's the use case:
I have a RootView tableview that uses NSFetchedResultsController to manage the list. I tap an add button, which presents the AdditionViewController. The AdditionViewController uses a separatemanagedObjectContext to create the new object. On Save, the object is passed back to the RootView, where the new object (in the new managedObjectContext) is merged into the main managedObjectContext. The AdditionViewController is then dismissed, revealing the RootView.
What I would like to do, is to push my DetailViewController with the new object loaded after the merge, so that when the AdditionViewController is dismissed, the full detail view is revealed.
How can I get the object that has just been added to the fetchedResultsController in order to pass it to the DetailViewController?
--UPDATE--
Still nothing on this. Let me try to explain what I need to do (hopefully) a bit better. If this is still confusing, ask questions. I'm having a hard time thinking of how to describe the problem.
I am fully aware how to push the Detail view underneath the modal addition view upon saving the new object. The problem is that the object I am saving is in its own fetchedReaultsController. I am merging this frc into the main fetchedResultsController, so if I try to sent the object to the detailview, I get a crash, because the object has been invalidated (due to the merge) by the time the modal addition view is dismissed, and the detailview calls viewWillAppear. That is what I am trying to get around. How can I figure out what object was just added to the main fetchedResultsController in order to send it to the detailViewController?
--UPDATE--
Adding a bounty for anyone who can tell me how to retrieve the most recently added object from a fetched results controller. Or how to retrieve a specific object from a fetched results controller without knowing it's indexPath.
Here's how I did it in an almost identical use case:
While the AdditionViewController is displayed, the user has the option of saving the item they created or cancelling out of the new item dialog. I communicated the user's choice back to the RootViewController.
If the user cancelled, remove the object you created from your context.
If the user chose to save, save the context and display the DetailViewController.
For the record, the answer was to grab the object ID of the object in the addingManagedObjectContext AFTER saving the context (since the ID changes after saving), and passing that ID to the main managedObjectContext after the merge. The full code required for this is below (if anyone has an easier way, let me know)
detailViewController.object = (customObject *)[[fetchedResultsController managedObjectContext] objectWithID:[[[[addingManagedObjectContext registeredObjects] allObjects] objectAtIndex:0] objectID]];
Thanks to frenetisch applaudierend for pointing me in the right direction.
Today my question is about UITableViewController-s
In particular I have noticed that the datasource delegate method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
is called twice (even if for instance I just create a navigation based application and without adding a line of code.. well adding an NSLog to track it).
Now, since in my application I need to determine the number of sections basing the choice on the documents in the file system, I need to call some methods to do so. I have put these methods in the above mentioned method, so they will be called twice, which is something I don't need.
The questions are why is it called twice, can I have it called once?
I hope that in the official documentation this is not clearly stated (which would mean that I didn't read it at all :) ). By the way I could see others posting similar questions, but I couldn't find a fully satisfying answer.
Thank you.
I was experiencing the same problem, only with the call to numberOfRowsInSection:
The answered laid in the stack trace for each call I received.
The first call was due to a change in the table header view I was making in the viewDidLoad: of my viewcontroller.
thumbView.tableHeaderView = nil;
thumbView.tableFooterView = nil;
This resulted in internal call to _updateContentSize: which called heightForTable: which eventually called numberOfRowsInSection:. This was something I triggered, and could be easily avoided by not doing the above code :)
The second call was the legitimate one in order to reloadData. This is triggered by a layout event somewhere and most likely you can't skip it.
I'm sure you can observe something similar for the numberOfSections: method
So, my conclusion is that due to the the implementation of UITableView there are many situations where certain delegate methods will get called twice or more because the table view has to refresh something. I try to design my code around this bug/feature/etc.
Hope it helps
If your tableview is contained by a child view controller,
Try this at your parent ViewController
[parentViewController addChildViewController:childViewController];
before [parentViewController.view addSubview:childViewController.view]
Please check your code, after adding TableView you may again called realodData method of table in mey be ViewWillAppear method
This can happen if you'r table view's frame gets changed by mistake in the story board.Say you clicked on the storyboard where you have added the table view as a subview and now your table may not be having the proper frame which you have set in the beginning.
I'm really having trouble getting a Cocoa Table View cell to send action messages.
At the most basic level, in IB there is an action assigned for the NSTextViewCell object, and after editing and pressing Return nothing happens.
So I have an IBOutlet hooked up to the NSTextViewCell, and have been experimenting with NSActionCell messages to it. But the Table View seems to pretty much just ignore them.
I've also tried subclassing NSTextViewCell, but the methods I'm seeing all look like they want to pass values to the object from somewhere, not return a value from inside the object to configure its behavior.
I'm pretty new to programming and Cocoa -- can someone explain each thing that needs to be overridden and how and where to do it?
AFAIK, the cells in an NSTableView won't send action messages out to your application, they're sent to the NSTableView so it can update its data. NSTableView itself tries to be pretty clever and update your data directly, rather than just telling you something changed, so depending on what you're trying to do and what the data source for the table is, you have a few options.
If you're using an NSTableViewDataSource object to populate the table, it's simple; just implement tableView:setObjectValue:forTableColumn:row: and the NSTableView will call that every time something is edited.
If you're using Cocoa data binding (for example, using an NSArrayController to bind an array of objects to the table,) then as long as everything is wired up correctly, the data should just automagically get updated in the source objects when the table is edited. If you need to take special action, then you can do whatever you need to in the property setter of your data class.
I haven't tried it yet, but could work...
NSCell *cellYouWant = [tableView preparedCellAtColumn:tableView.clickedColumn row:tableView.clickedRow];
I've an NSTableView that uses the controller object for the NIB being displayed as the data source. I implement the NSTableView informal protocol.
This NSTableView gets its values from Core Data. I startup the application, load all values I have in XML and then display them.
My problem is, the NSTableView doesn't seem to add any new rows to the end of the table. If I start the application with no values in permanent storage and add another one (adding values works as I can see them being saved to XML), the table view simply ignores the new value.
If I add a value I know will go to the end of the table (the contents are organized alphabetically), I won't see the new value.
If I and a value that I know won't go to the end of the table, the value will be added, I will see it on the table, but the last value on the table will be pushed out and disappear.
I've noticed that - (int)numberOfRowsInTableView:(NSTableView *)tv is only being called when the application starts up and not when I do [tableView reloadData]. What causes this event to be fired? I tried firing it by hand before calling reloadData on the tableView but doesn't seem to work.
Any ideas to what might be causing this?
Has anyone encountered something like this? Any clues to what might be?
Alex's comment made me review the code, specifically bindings in Interface Builder. Turns out I had set the bindings between the table and my controller object and implemented the NSTableDataSource.
I've removed all bindings and only implement the NSTableDataSource protocol.