View-based NSTableView renders blank rows after inserting new rows with animation - cocoa

I have a view-based NSTableView that I'm backing with an `NSMutableArray . Periodically I go out grab some data and want to insert new rows into the table at the top.
When I do this without specifying an animation to insertRowsAtIndexes:withAnimation: it seems to work fine for hours on end. However, if I specify an animation, after about 7 or 8 inserts the table view starts to show blank rows where the inserts occurred. Scrolling down and then back up causes the new rows to render properly.
The code that calls insertRowsAtIndexes:withAnimation is in a block, and not running on the main thread, but my inserts happen inside of dispatch_async on the main queue, so I dont' think it related to multithreading.
Here is some code ... self.contents is my NSMutableArray.
dispatch_async(dispatch_get_main_queue(), ^{
[self.contentsTableView beginUpdates];
if(posts.count) {
for(NSDictionary* dictionary in [posts reverseObjectEnumerator]) {
FNPost* post = [[FNPost alloc] initWithDictionary:dictionary width:self.contentsTableView.frame.size.width];
post.delegate = self;
[self.contents insertObject:post atIndex:0];
[self.contentsTableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:0] withAnimation: NSTableViewAnimationSlideLeft];
}
}
[self.contentsTableView endUpdates];
});
One thing I'm confused about is the part of the Apple NSTableView documentation for insertRowsAtIndexes:withAnimation that says:
The numberOfRows in the table view will automatically be decreased by
the count of indexes.
I'm confused by what that statement implies about the relationship between the number of objects in my array (and hence the result of numberOfRowsInTableView:) and the number of rows the table view thinks it has. To my thinking the number of rows in the table view should equal my array count and I want to make sure that my understanding of this is not at the root of the problem but like I said, the table works fine if no animation is specified.
Am I doing something obviously wrong here?

This issue turns out to be caused by having the NSTableView in a layer based view hierarchy. Two superviews up, I had a layer-backed view. After reading some other posts about NSTableView showing other errant behavior when in a layer-backed hierarchy I decided to turn off the layer in IB. Table animations now occur as I would expect at the expense of having to maintain some additional state to control the layer backing state when I want to use CA transitions between my views.

Related

NSTableBackgroundView

I have a ViewController which has a single TableView within its content.
Within the ViewController I access the actual Table via:
NSView * myView = self.view.subviews[0];
myView = myView.subviews[0];
NSTableView *myTable = myView.subviews[0];
[self.targets sortUsingDescriptors: [myTable sortDescriptors]];
[myTable reloadData];
This actually is clumsy but seems to work. Except that when I mouse down within the table when the vertical scroll bars are active. Then, I raise an exception because in the final line of accessing subviews to reach myTable, the subview array has several NSTableBackgroundViews before the actual TableView that I created and want to access.
NSTableBackgroundView apparently is not documented and does not have a sortDescriptors method (hence the exception).
Perhaps someone can point out a better way to get to the table which is more reliable, or can someone say whether enumerating the final subview array to find the desired ClassOf NSTableView is safe?
Many Thanks!
in lieu of any other answers, I implemented planB, I took the array of subviews and enumerated on them to find the actual NSTableView. This seems to work when I bang on the UI to test it and no more flags are raised.
I wish there were a cleaner way I could discover to get the view I want without traversing all the subviews.

Pre-creating UICollectionView cells - layout issue

I have a UITableView with 6 rows. Each row contains a single UICollectionView. Each collection view contains a single section with 10-15 cells. One view controller is the datasource and delegate for both the table view and the collection view.
I would like to address some performance issues when scrolling the tableview. Whenever a new section comes into view, there is a small pause while the collection view is created and filled. Since I have a fixed number of cells (< 100) and they are almost static (they are initially loaded from a web API but the data will change only a couple of times a week), I would like to pre-build each of the collection view cells in advance. I would prefer the user waits an extra half-second on launch than encounters jerky scrolling.
To accomplish this, I have updated my collectionView: cellForItemAtIndexPath: to check a dictionary of cells I am maintaining. It looks for a key composited from the collection view index and the indexPath for the cell. If the key exists, the corresponding object is returned. If none is found, the cell is built and also added to the dictionary. This step effectively prevents cells from being un-loaded and recycled, at the expense of using more memory.
But on launch, I still need to run this once for each cell to pre-populate the dictionary. I iterate over each table view cell, find the collection view, and call
[self collectionView:collectionView cellForItemAtIndexPath:indexPath];
This is almost enough. The cells are being created and stored in the dictionary, and when I scroll to a new collection view, I see that they are being pulled from the dictionary and are displayed. But all of the cells, and all of their contents, are shoved up in the top-left corner at {0,0}.
Some logging tells me that at the time the cells are created, the frame of the collection view is {{0, 0}, {0, 0}}. I assume this is why none of my layout is being applied?
How can I resolve this?
(Would also be interested in any general comments on my pre-loading implementation).
I resolved this by calling [cell layoutIfNeeded] on the UITableViewCell (not the collection view). A more thorough explanation is welcomed.

UITableView Scroll when new cells are added

In my app I have a pull down to refresh feature. Basically what this does is it adds some more cells to the top of my UITableView. However when I do this the screen automatically scrolls down. I want to keep my position on the screen when the new cells are loaded.
[dataPosts insertObject:[[MainButton alloc] initWithFrame:CGRectMake(0, 0, 320, 260) withData:0 hasNick:[self createRandomStringWithLength:140]] atIndex:0];
dataPosts is the data array of my UITableView. MainButton is a UIView with size 320,260 and it is added as a view into a UITableViewCell. So whenever I add a new entry to my data, the table automatically scrolls down 260 units but I want my position to stay the same. Thanks in advance.
-C
When the new rows are to be inserted you are aware how many rows are going to be inserted. So based on that you can call scrollToRowAtIndexPath:atScrollPosition:animated: method of UITableView to be at the same place even when new rows are inserted at the top.
E.g:
Currently 10 rows are displayed and 1st row is at the top.
Now suppose, new more 20 rows are to be inserted. Insert them without animation.
Now, call above mentioned method with indexPath.row of 21 without animation.
Hope that helps. Let me know if you are looking for something else.

how to keep the visible content after nstableview reloaddata?

I have a subclassed nstableview whose data source array may increase, by calling reloadData: I can refresh to reflect the data updating.
But after reloadData:, the tableview will always scroll to the new cell with the same old row number (for example, if the tableview was showing the 2nd cell, after reloadData:, the tableview will scroll to the new 2nd cell, therefore, the visible content of the tableview will change). How can I disable this automatic behavior and keep the visible content unchanged after the updating?
thanks in advance!
Table views don't re-scroll when their content changes. Since you're changing the table content which underlies the currently displayed rows, you need to move the scroll yourself.
I don't have code for this, but I suggest using rowAtPoint: to find the initial position of the table view, identifying or calculating the new row index for that row's content, and then calling:
[tableView reloadData];
[tableView scrollRowToVisible:newIndex];
I got this problem fixed: first, I got the rect size changed amount; then after the reloadData: , scroll the tableview to the changed point (original point + delta parts) immediately with the clipview's scrollToPoint: method. It does it so fast that you cannot realize the operation there.

Have an NSTableView row always stay in the same place

Im trying to make a trash can system in my app. I have a NSTableView and a trash can row. I want to have the row "stick" to the bottom of the visible table view so it can always be seen. Is it possible to do this and if not is there a better approach to doing this?
Thanks for any help
Sit another utterly separate UIView, on top the views that contain the table.
Simply fake it up to make it look like a row from the table.
As you say, "Thanks that sounds like it will work as long [tableview deselectRow:[tableview selectedRow] works so it appears that the fake row is really in the view." -- that is precisely what you do.
There's no supported way to do this and it might be near impossible. You might try embedding your "main" table view in a container NSView (that observes the table view's frame changes resizes / performs layout when the size changes). This view would leave room at the bottom for a single-row table view with no headers. You could feed the "main" table view all but the sticky row, and feed only the sticky row to the smaller table.

Resources