JSON, cellForRowAtIndexPath bug - objective-c

Struggling to find where fault is with my code. On first view load, everything works and loads fine as it should, but when i revisit that view, it seems that the first two cells are empty. I logged the dictionary (dict) in viewWillAppear: and it logs the data fine, so error has to be in cellForRow method. Take a look at my method, and see where i'm going wrong, the third cell populate third piece of data, so i'm totally stumped, but the first two cells are completely blank, no data.
http://pastebin.com/Va84MG5g

First of all, why are you doing all of that insane UITableViewCell customization inside of your tableView:cellForRowAtIndexPath: method? Create a custom UITableViewCell subclass and do the set up there.
In the class's initWithStyle: method, add all of your subviews with a frame of CGRectZero, because at initialization, the table view cell doesn't know how big it is. You can set text alignments, colors, etc. here as well. Then, in layoutSubviews, go ahead and set all the frames. Override prepareForReuse and set things like your UIImageViews to nil. This will help with performance for reused cells.
As for why you're not seeing your data in your first two cells, my initial thought is that it has something to do with the way you're setting up your cells for reuse. You're asking your tableView to dequeue a regular UITableViewCell and only creating all of these subviews if the returned cell is nil. So what happens when it returns a UITableViewCell? You skip the part where you alloc/init all these subviews, and so you're basically adding nothing to the cell. I feel if you create a custom subclass and ask your UITableView to dequeue that instead, you'll get the result you're looking for.
NOTE: If you're targeting at least iOS 5, you can create your UITableViewCell's layout in a nib and register the nib with the table view. Doing so will guarantee that you always get a dequeued cell, and you never have to do your if (cell == nil) check. If you're targeting iOS 6, you can register a UITableViewCell subclass.

Related

Use single UITableViewCell subclass across multiple UIViewControllers

So I have this UITableViewCell subclass that has some really complicated logics in it - it triggers some actions in UIViewController it's actually attached to. Of course the cell is not aware of its UIViewController but I still navigate to it like this:
UITabBarController *tabVC = (UITabBarController *)appDelegate.rootVC.centerPanel;
SGFirstTabViewController *firstTab1 = [tabVC.viewControllers firstObject];
[firstTab1 reloadCell:self];
The thing is now that I want to use the same subclass of UITableViewCell around about 5 different UIViewControllers.
What's the best way to do this? I will almost never know what is the UITableViewCell's VC is and I simply can't create 5 different cell subclasses with the same code over and over. What's the best way around it?
I think there is perhaps some misunderstanding in the role of the cell in the model-view-controller programming pattern.
The cell should not reload itself but it should be told to reload by the view controller that controls it. The reload code in the cell can stay the same.
So, rather than the cell having to find out which one its view controller is, have the view controller listen to the cell action (e.g. via delegate methods) and fill it with the appropriate reload data as directed.

UITableViewCell Height Calculation and delegation?

I know this question had been asked hundreds of times before, but it's never really been solved (Or at least the way I'd like it to be). I have a rather complex UITableViewCell setup. The cell.backgroundView is loaded from a UIView subclass which uses a fair bit of CoreGraphics and CoreText. The code is rife with CTFramesetterSuggestFrameSizeWithConstraints, so I'm relectant to duplicate the class in the heightForRowAtIndexPath method.
I think I can solve this by creating an NSMutableDictionary with the indexPath as the key and the height as the value. But then I'm faced with the problem of heightForRowAtIndexPath being called first. I believe I can solve this problem by guessing the height of the cell and then once the cell's UIView subclass has finished rendering, use delegation to set the cell's height.
But this leaves me with the problem, how the hell do I delegate this? And, how to I prevent scrolling from being extremely choppy? as the cells will be created and resized in a split second.
In the past, I've used a dummy cell. I have a separate method -fillInCell:forRowAtIndexPath: which puts data into the cell. That way I can fill out the dummy cell in -tableView:heightForRowAtIndexPath: and the real cell in -tableView:cellForRowAtIndexPath:.
If this does not work for you then there are other options.
The first thing that comes to mind is create real cells in -tableView:heightForRowAtIndexPath: instead of -tableView:cellForRowAtIndexPath:. You can store completed cells in a mutable dictionary. -tableView:cellForRowAtIndexPath: will simply pull the completed cell from the dictionary. You should also detect when scrolling has stopped so you can empty your dictionary (just because -tableView:heightForRowAtIndexPath: was called doesn't mean -tableView:cellForRowAtIndexPath: will get call for the same indexPath).
Hope that helps.

Receiving notification on a UICollectionViewCell's (or UITableViewCell's) removal from its parent collection or table view

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.

UITableViewCell Accessory Views Duplicate In Defferent Sections When Scroll The UitableView

This question make me troubled for a long time, I want to implement defferent accessory views on different cells in my tableview.
And my tableview have multiple sections, But they duplicate onto other cells in my other sections that I have not implemented when I scroll up and down the UITableView. I know someone said "It should be Use different identifiers when you assign dequeueReusableCellWithIdentifier: for each cell in cellForRowAtIndexPath: method before,
But I don't know how to implement programmatically in detail, Anybody can write a sample code in detail help me to understand?
Thank you very much!!!!!
Its all well and good to use different accessory views. But you have to keep in mind that if you are recycling cells, then you first try to recycle, if none available you create a cell, THEN you need to set the appropriate accessory view for the particular cell you are going to return. Who knows what the old one was for a recycled cell...

Unload specific UITableViewCells

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.