I'm trying to make a UICOllectionView with a stack of subviews, with the possibility to add and delete different subviews.
The problem is that cellForItemAt is not called properly. E.g. numberOfItemsInSection is set to 4 so I would expect 4 subviews. However, cellForItemAt is not called for all items, only for visible, and it is being called only when I scroll through this UICollectionView. Is there any way to call it for all items at once ?
Another problem is that after scrolling through UICollectionView to the end it adds additional subview, which corresponds to one of the previous subviews.
So it is not clear, why cellForItemAt is called as soon as all items were shown. And how it choses indexPath.row for this additional item.
The last strange thing is that cellForItemAt is being called once again as I scroll back , so it adds subviews to the cells which already have some subviews so that they are stacked one above another:
It seems, that the way UIcollection works is completely different from principles in UITableView
UICollectionView isn't that different really to UITableView really.
'cellForItemAt' is called each time a cell is about to become visible so on first load it will only be called for your visible cells, which is the behaviour your seeing. As you scroll through it will be called for each cell as they appear. This makes it more memory efficient as cells are reused and only created when needed.
I'm not sure you can force it to load all cells initially without a bit of hacky code to either scroll the collection view yourself or programatically call the code you want yourself. Without understanding your implementation better this probably isn't what you want.
The additional cellForItem call when you scroll to the end is probably due to the collection view 'bouncing' on the last cell and reloading a previous one due to the animation. Also 'indexPath.row' should probably be replaced with 'indexPath.item' for collection views but this shouldn't really affect your implementation.
Related
I have a basic UITableView that contains 20 cells, one of the cells is a MKMapView just dropped in as is without any custom code (not even setRegion:Animated), if I open the view at the first time and scroll down the table view (towards the map's cell), there is a noticeable hang happens for the app, and if I use setRegion:Animated (without dropping any pin) the hang gets longer. However, this block of the main thread disappears on later attempts to scroll towards the map's cell.
I can't use the MKSnapShotter because I want the user to interact with the map so an image won't satisfy the case.
the table view does not make any block on the main thread if the map does not exist.
how to avoid blocking the main thread while showing the map's cell for the first time ?
First make a tableView IB outlet if you haven't already done so. Create the cell in view did load using tableView.dequeueReusableCellWithIdentifier("mapCellIdentifier") and store that in an ivar. Then in the data source return the cell when the index path is the one you want so inside of cellForRowAtIndexPath do
if (indexPath.row == wantedRow) {
return mapCell
}
The mapCellIdentifier needs to be set in the cell you created in interface builder for the map. This is assuming you have dynamic cells.
While using collection view, in cellforItemAtIndexPath, new cell instance is being added multiple times (on top of another) at same location/frame, even though correct reuseidentifier is being passed to "dequeueReusableCellWithReuseIdentifier:forIndexPath:"
The cell mentioned above is a subclass of UICollectionViewCell and contains UITextField with proper frame. When scrolling and textfield is first responder, the above said problem is occurring.
Please let me know of any pointers to address the issue.
This could be a bug in UICollectionView related to one I've already filed with decoration views. As long as your cells are opaque, it should not affect your interface
It's possible (though, imo, not very likely) that this is correct behaviour for UICollectionView and it uses those extra cells for interface orientation. At any rate, the problem seems much less pronounced that the one with decoration views, which would add dozens of copies of the decoration view. As long as it's not affecting your app, I'd say live with it.
I have problem with UIScrollView, I try create sth like table with own rows ( separate nib ). Everything works good with 1-10 rows, but problem occurs with more than 20 elements. The application starts working slow, and stunt. Is there any solution to optimalize scroll view for 100-200 own subviews?
Use UITableView. That's precisely what it's designed for.
UITableView and UICollectionView are both optimizing by removing subviews that are no longer needed and putting them in a reuse queue. By reusing those views the system does not have to create and destroy their backing layers but can reuse them. This way you only have ever as many subviews on screen as can fit.
Typically you want to add/remove visible subviews in either the layoutSubviews of a scroll view subclass or the corresponding didScroll delegate method. Personally I prefer the layoutSubviews since it is a bit earlier in the chain of events.
Basically you would get a reusable subview from your reuse queue as soon as at least 1 px of the subview should appear within the bounds of the scroll view and remove the subview as soon as no pixel of it is visible any more.
If you use UITableview or UICollectionView instead of a plain scroll view they provide a mechanism to register views in NIB for certain reuse identifiers and then the dequeueing will automatically load a new instance of a subview from NIB is none is queue or dequeue one if there is.
Background:
I have a UICollectionViewController that shows items in one of two modes, which the user can toggle between. Each mode uses a different class of UICollectionViewCell. Let's call these modes "list view" and "grid view".
When I switch modes, I call .reloadData on the UICollectionView, which redraws the collection view using the correct cell classes. Everything works fine here.
Now: Inside the UICollectionViewCell subclass for one type of cells, I want to be notified when the collection view that contains it switches modes. Visually, a cell which was on-screen vanishes; the collection view is drawn fully with the other type of cell. When switching back, the cell is re-displayed.
Question:
How can I be notified when a UICollectionViewCell is "removed" (i.e., no longer shown; I'm not sure what's happening under the hood yet) from its parent collection view?
Notes:
prepareForReuse is not called on the cell when the collection view's updateData causes the cell to no longer be included.
willTransitionFromLayout:toLayout: (an empty layout?) is not called.
Overriding didMoveToSuperview is of no help; it is not called.
Observing .hidden or .alpha on the cell does not work.
The cell's dealloc is not called; it sticks around in the reuse queue.
Something in the cell must be changing that I can observe or hook into, what is it?
Update: UICollectionViewDelegate has this method, which from the documentation seems like it does what I am asking:
collectionView:didEndDisplayingCell:forItemAtIndexPath:
Original answer:
I got this working as desired by having the UICollectionViewController manually notify visible cells of impending doom with this method when I'm about to toggle and call reloadData:
- (void)notifyCellsWillBeHidden {
for (UICollectionViewCell *cell in self.collectionView.visibleCells) {
if ([cell respondsToSelector:#selector(willBeRemovedFromCollectionView)]) {
[cell performSelector:#selector(willBeRemovedFromCollectionView)];
}
}
}
These cells can then do what they need to do if they implement the above method.
Calling prepareForReuse may not a good idea because it will be called again before the cells are re-displayed, if this is a problem.
I'm still curious whether there is a way for a cell to receive notification that it is going to be made non-visible without an explicit call.
Is there a way to unload/release a UITableViewCell such that the containing UITableView calls cellForRowAtIndexPath: when it is needed again?
I understand that this is exactly what UITableView does by default, but only once the cells are outside the tableview frame. My custom view uses UITableView in such a way that its frame == content size.
If there is no way to unload specific cells, I'll have to think of a different approach.
The method [UITableView reloadRowsAtIndexPaths:withRowAnimation:] allows you to tell the table view to reload one or more cells. This will reload the specified cell(s) if they are on screen and presumably do nothing if they aren't currently on screen. Sounds like what you want though your question is not entirely clear to me.