I’ve got a UITableViewController which has a table view with custom cells (they have a subclass and a xib). The thing is that I load data from a blog and it shows up ok, but when I try to scroll or touch, the cells reset, like if they weren’t loaded.
This is how it appears:

And this is what happens when I try to scroll:

This is my View Controller code:
#import "TLDRCategoryViewController.h"
@interface TLDRCategoryViewController ()
@property(nonatomic,strong) IBOutlet UIView *headerView;
@property(nonatomic,strong) NSMutableArray *posts;
@property(nonatomic, assign, readonly) TLDRTag category;
@property(nonatomic, strong) IBOutlet UILabel *category_label;
@end
@implementation TLDRCategoryViewController
@synthesize headerView = _headerView, posts = _posts, category = _category, category_label = _category_label;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
-(void)setCategory:(TLDRTag)_cat {
_category = _cat;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.tableHeaderView = _headerView;
_category_label.text = [TLDRHelper showableNameForTag:_category];
_category_label.textColor = [TLDRHelper colorForTag:_category];
TLDRRetriever *retriever = [[TLDRRetriever alloc] initWithDelegate:self];
[retriever postsByCategory:_category];
if (_posts == nil)
_posts = [[NSMutableArray alloc] init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)postsLoaded:(NSDictionary *)postsDict {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void) {
for (NSDictionary *key in postsDict[@"response"][@"posts"]) {
TLDRNewsItem *newsPost = [[TLDRNewsItem alloc] initWithDictionary:key];
[self.posts addObject:newsPost];
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.posts count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"TLDRNewsCell";
TLDRNewsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"TLDRNewsCell" owner:nil options:nil];
for(id currentObject in topLevelObjects) {
if([currentObject isKindOfClass:[TLDRNewsCell class]]) {
cell = (TLDRNewsCell *)currentObject;
break;
}
}
}
cell.title.text = ((TLDRNewsItem*)self.posts[indexPath.row]).articleTitle;
cell.content.text = ((TLDRNewsItem*)self.posts[indexPath.row]).articleTLDR;
return cell;
}
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text = ((TLDRNewsItem*)self.posts[indexPath.row]).articleTLDR;
CGSize textSize = [text sizeWithFont:[UIFont systemFontOfSize:14.0f] constrainedToSize:CGSizeMake(self.tableView.frame.size.width - PADDING * 3, 1000.0f)];
return textSize.height + PADDING * 3;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
And this is the relevant part of TLDRRetriever:
-(void)postsByCategory:(NSInteger)cat {
NSString *string_cat = @"";
switch (cat) {
case TLDRTagWorldNews:
string_cat = @"world%20news";
break;
case TLDRTagBusiness:
string_cat = @"business";
break;
case TLDRTagDesign:
string_cat = @"design";
break;
case TLDRTagEntertainment:
string_cat = @"entertainment";
break;
case TLDRTagFacts:
string_cat = @"facts";
break;
case TLDRTagHealth:
string_cat = @"health";
break;
case TLDRTagPolitics:
string_cat = @"politics";
break;
case TLDRTagScience:
string_cat = @"science";
break;
case TLDRTagSports:
string_cat = @"sports";
break;
case TLDRTagTech:
string_cat = @"tech";
break;
case TLDRTagWeb:
string_cat = @"web";
break;
default:
string_cat = @"";
break;
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@", kTLDRAPIURLCats, string_cat]]];
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
[[self delegate] postsLoaded:json];
});
}
I’ve got an almost exact view controller on that app as well and it works fine (I’m using most of the code of that controller, and this one will be reusable for other similar categories). This controller is allocated in batch (I allocate 11 of this controllers and save them in an array). I couldn’t debug this, so I don’t know what’s going on. Is there something wrong in the code? Thanks!
The fix wasn’t related with the controller or anything like that: it was the root view controller. The views weren’t being retained in memory, so I just made a ivar for every controller instead of looping through them and that fixed the app 🙂