UICollectionView stops loading cells after moveItemAtIndexPath:toIndexPath: - ios

I have a UICollectionView with UICollectionViewFlowLayout which contains many items. I want to reorder a cell due to the user input.
[self.collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
After execution of this code, the collection view doesn't update it's contents when scrolling. It doesn't show next cells. It doesn't even call cellForItemAtIndexPath: but it's content size is correct (the layout "updates" it's state).
Therefore we can only see the cells which were distributed on the collection view before the reorder.
Any clues what might be the issue ? Are there any known workarounds for this issue? The app is iOS 8 only and the collection view is used with the UICollectionViewFlowLayout.
EDIT: When I call [self.collectionView reloadData]; the datasource is not even asked for anything, too.

The issue was with the UICollectionViewCell. High level description is that the cell is sent a method actionForKey from my code during the move animation. It caused the cell to stay in internal being animated state. I couldn't find reasonable workaround and refactored some bits in order to avoid actionForKey call. Now everything works as expected.

Related

iOS infinite scroll items disapearing (sometimes)

This is a very rare occurring bug from hell,
I have an infinite scroll controller that displays products, 2 in each row. Rarely, something affects the controller and causes items to vanish, when I tap the empty area where the item should be, it works as expected and directs the user to the item details controller. When I back out back to the list, sometimes the cell shows its content, and others get hidden.
Sometimes it just a couple of items missing, sometimes there are so many missing items that makes the list appear empty, like only 1 or 2 cells are visible per screen height.
An even stranger situation is, when I scroll really fast to the end and stretch the screen really fast out of the visible area, and there are no more items to load, the visible items can jump from left to right.
Please see these two videos.
https://www.dropbox.com/s/jibflcouz1ena8n/missingProductImages.mov?dl=0
https://www.dropbox.com/s/uz13fzorypnp38t/again.mov?dl=0
I could send code but I didn't want to clutter this place with full length code, let me know if you want to see a specific section of the code please. Maybe someone could have an idea of what might be going on by looking at the vids.
What's probably going on here is a state problem with your collection view cells. The code assigning model values to the cell's views would be of use here. But absent any actual information, the first thing to review would be the cell's prepareForReuse implementation:
Does it call super?
Does it clear out all current values?
Does it cancel any pending asynchronous operations?
Are fetches and cancellations correctly matched?
Next, check if there's any essential configuration in the cell's init/initWithCoder methods -- those are only called on first creation, not on reuse.
Those are the normal pitfalls of UICollectionView cell handling. If they don't suggest the problem, please post the cell code.
It looks like your cells are not being reused correctly.
Can you check that you have set the same reuseIdentifier for your cell in Interface Builder that you are assigning in your code?
Update: Attached image to show where to set the identifier in the storyboard/xib
Update: added layout solution
Another problem could be due to the layout bounds of your collectionViewCell. When you load your cells they bounds are not calculated until they have been added to your collectionView and rendered. This can cause the elements in your cell to layout with the wrong values. This happens commonly with async image frameworks as they cache a resized version of the image for performance. When you cell loads, the cached image is loaded at the wrong the wrong size.
Add the following code to your collectionViewCell:
- (void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
self.contentView.frame = bounds;
[self setHighlighted:NO];
}

iOS Collection view cell update is delaying

I have sample collection view. I am dequeue cell from it by dequeueReusableCellWithReuseIdentifier:forIndexPath:
When ViewController is loaded the OS dequeue 5 cells and load information on it.
When I scroll, another 5-7 cells are dequeued and information is loaded on it.
The second set of loaded cells is coming in foreground with information from first set of loaded cells and the information in them is updated with fresh (right) one after less than a half second.
I am using prepareForReuse for cells, where I reset visibility of all cell components and than load fresh information, but the result is not what I expect, it is what I described above.
Any suggestions?
Thanks.
EDIT: Well if I update cell views with data from dequeueReusableCellWithReuseIdentifier:forIndexPath: all works fine, but if I pass the data to cell and ask cell to modify itself - than I have a problem pointed above.
EDIT 2: Soved. As I imagine - it was stupid mistake. As advice for others - make sure that you pass data to cell in any cases.
Do you have ALLOC statements in cellForItemAtIndexPath? or if using a subclass, do you have any ALLOC's in the subclass implementation? Possibly you seeing ui elements that are not being cleared out.

Resize TableCell on select/deselect

I'm new to iOS and MonoTouch, and I have a pretty basic need - I have two vastly different views to display in the same table cell depending on whether it is selected or not. When you load the app I'll present a list of products in a custom UITableView, with all the rows deselected and each row basically just showing the product names in a label. As soon as a user taps one of the rows and selects it, I show a very different view with more of a shopping cart layout. Since the two views are so different they have vastly different sizes. What i need is for the cell itself (or row?) to grow and shrink according to the natural height of whichever view is currently being displayed.
I'm using descendants of UITableViewController, UITableViewSource, and UITableViewCell for the solution. What I do is add both versions to the cell's ContentView, then set the Hidden property as needed when the cell/row is selected/deselected to show the right layout. That works well. I'm in demo mode right now, so I'm not overly worried about efficiency or responsiveness, that will have to come later.
I know that GetHeightForRow is responsible for sizing the rows but it only gets called once, when the cell is first shown. It seems to me that I need to alert the TableView to re-poll the source for a new size as the views are changing, but I can't figure out how to do it.
I tried (rather hopefully) to cause GetHeightForRow to be invoked again manually using Cell.SetNeedsLayout and Cell.SetNeedsDisplay, hoping that would cause the table to re-query the source for new dimensions, but no joy.
I tinkered with the direct approach, trying to size the contentview itself but it didn't seem as if it was leading anywhere. I feel as if the table needs to be told to query for a new row size, but I'm open to any suggestions.
Surely I'm not the first to attempt this? What am I missing?
Try forcing GetHeightForRow to be called again by calling ReloadRows instead.
You may or may not have to specify begin/end updates (this might just be for animation, though?)
tableView.BeginUpdates();
tableView.EndUpdates();

Disable cell reuse on UICollectionView

I am using a UICollectionView in my app with gesture recognizers on the individual cells which allow the user to "slide open" the cell to reveal more data underneath.
The problem is, I am reloading the data in the CollectionView very often; as the app receives updates once every 3 seconds or so. This results in unwanted behavior with the collectionview cells being reused while a cell is in the process of being slid.
The user will start to slide a cell, the app will receive an update, reloadData, and a different cell will start receiving the gesture instead, and begin sliding.
I have tried disabling the app's updates while the slide is occurring, but that caused other complications within the app, so I am wondering if there is a way to disable the cell reuse, (I will only have 20 cells max, so I don't think there would be a large drop in performance).
Thank you!
Why don't you use a flag like needsReload and set it, if new data is available. After a slide you check for that flag and reload the collectionView, if needed? Is this not working?
If you don't want cell reusing, just use a default scrollView and put all your views in it!?
Disabling reuse is simple. Just don't use the dequeueReusableCell method.
Instead just alloc, init your cells. I would be careful of the performance and memory implications of doing so though...

Graphical glitches when adding cells and scrolling with UITableView

I am using a UITableView to display the results of a series of calculations. When the user hits 'calculate', I add the latest result to the screen. When I add a new cell, the UITableViewCell object is added to an array (which is indexed by tableView:cellForRowAtIndexPath:), and then I use the following code to add this new row to what is displayed on the screen:
[thisView beginUpdates];
[thisView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation: UITableViewRowAnimationFade];
[thisView endUpdates];
This results in the new cell being displayed. However, I then want to immediately scroll the screen down so that the new cell is the lowermost cell on-screen. I use the following code:
[thisView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
This almost works great. However, the first time a cell is added and scrolled to, it appears onscreen only briefly before vanishing. The view scrolls down to the correct place, but the cell is not there. Scrolling the view by hand until this invisible new cell's position is offscreen, then back again, causes the cell to appear - after which it behaves normally. This only happens the first time a cell is added; subsequent cells don't have this problem. It also happens regardless of the combination of scrollToRowAtIndexPath and insertRowsAtIndexPath animation settings.
EDIT:
I've now started inserting cells at the second-to-last position of the table, rather than the end, and the problem still occurs - when first inserted, a cell is 'invisible' until it goes offscreen and comes back on again. What could be causing this, and how can I force the cell to be drawn as soon as it is added to the table?
You're having problems because your updating the table without updating the data model backing it. Tables don't actually know how many rows they have nor what cells to display. They depend on the datasource and the delegate to tell them these things. Your design expects the table itself to track them.
insertRowsAtIndexPaths: is intended to be used for moving existing rows around a table, not for adding entirely new logical rows. When you insert an entirely new cell, the tableview looses track of how many rows it actually has.
Before you display a new row, the first thing you should do is update the values returned by:
– numberOfSectionsInTableView:
– tableView:numberOfRowsInSection:
... to reflect the addition of the new rows. This will allow the table to understand how big it is.
Then you need to update cellForRowAtIndexPath: to return the correct cell for the added row. Then you need to reload the table.
After you've done that, you should be able to scroll the tableview to the end and have the cell display properly.
The important thing to remember about tables is that they are dumb. The table itself holds no data, doesn't know how many sections and rows it has or what order the rows and sections come in. All the logic about data, sections, rows, cells and cell contents comes from the datasource and/or the delegate. When you want to change a table, you actually change the datasource and/or the delegate and then the table will reflect those changes automatically.
Edit:
Upon rereading the parent, I see that your putting the actual UITableViewCell objects in your data array and that you have one cell for each row.
This is not how tableviews are supposed to work and this will not scale beyond a few dozen rows at most.
Tableviews are intended to be an illusion that allows you display a lOGICAL table which has an arbitrary high number or rows. To that end, it only keeps enough UITableViewCell objects alive to cover the visually displayed area in the UI. With a default cell height of 44 pixels this means a tableview will never have more than 9 cell objects at a time.
Instead of eating memory holding cells that are not displayed, the tableview lets the delegate dequeue a cell that has scrolled off screen, repopulate it with the data of another LOGICAL row and then display it in a new position. This is done in cellForRowAtIndexPath:
You really need to start over here with your design. Your data needs to be kept separate from the user interface objects. You don't want to have more cells alive at anyone time than absolutely necessary because your memory use will balloon and your response time will degrade. Your current problem is the result of this unusual design.
When you've done that, you can add the result row as outlined above.
Try to scroll with some time shift after cell update via NSTimer or performSelector:withDelay:. It can help but to fix all problems I think there need to do more work.
The glitches may be caused because a UITableView considers itself the owner of any UITableViewCell instances it is displaying, and reuses them as needed. Part of that process is calling prepareForReuse on the cell. Since you are keeping the cells in an array, you do not want them reused. Try implementing an empty prepareForReuse in your UITableViewCell class. Or just create cells dynamically in tableView:cellForRowAtIndexPath: as apple recommends.
I used what Skie suggested to avoid the problem in the following way:
Immediately after adding the row:
[self performSelector:#selector(scrollToDesiredArea:) withObject:newIndexPath afterDelay:0.4f];
This called the following:
-(void)scrollToDesiredArea:(NSIndexPath *)indexPath{
UITableView *thisView = (UITableView*)self.view;
[thisView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
The delay of 0.4s seems to be sufficient to avoid the glitching; any less and it still happens. It may have to be different on varying models of iPhone hardware, though - I only tested on emulator.

Resources