How to Asynchronously load UITableViewcell images so that scrolling doesn't lag - ios

I've tried using ASyncImageView for this purpose, but I'm a bit confused as to how I'd implement it for my specific case. I currently have a MatchCenterViewController that contains a table inside of it. It's loading the images for the cells synchronously, which is causing a lot of lag when scrolling through the table. How can I modify the way I'm loading the remote images so that it's done asynchronously? My code is below:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#import "MatchCenterCell.h"
#interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *matchCenter;
#property (nonatomic, assign) BOOL matchCenterDone;
#property (nonatomic, assign) BOOL hasPressedShowMoreButton;
#end
#implementation MatchCenterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_matchCenterDone = NO;
_hasPressedShowMoreButton = NO;
// Set up MatchCenter table
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewCellStyleSubtitle];
self.matchCenter.frame = CGRectMake(0,70,320,self.view.frame.size.height-100);
self.edgesForExtendedLayout = UIRectEdgeAll;
self.matchCenter.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
self.expandedSection = -1;
_matchCenterArray = [[NSArray alloc] init];
// Refresh button
UIImageView *refreshImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"refresh.png"]];
refreshImageView.frame = CGRectMake(280, 30, 30, 30);
refreshImageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(refreshPressed:)];
[refreshImageView addGestureRecognizer:tapGesture];
[self.view addSubview:refreshImageView];
// Preparing for MC and indicating loading
self.matchCenterArray = [[NSArray alloc] init];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
[self.view addSubview: activityIndicator];
[activityIndicator startAnimating];
_matchCenterDone = NO;
// Disable ability to scroll until table is MatchCenter table is done loading
self.matchCenter.scrollEnabled = NO;
[PFCloud callFunctionInBackground:#"MatchCenter3"
withParameters:#{}
block:^(NSArray *result, NSError *error) {
if (!error) {
_matchCenterArray = result;
[activityIndicator stopAnimating];
[_matchCenter reloadData];
_matchCenterDone = YES;
self.matchCenter.scrollEnabled = YES;
NSLog(#"Result: '%#'", result);
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _matchCenterArray.count;
}
//the part where i setup sections and the deleting of said sections
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 21.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 40;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
//code snipped out for conciseness
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
//Header code snipped out for conciseness
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *currentSectionDictionary = _matchCenterArray[section];
NSArray *top3ArrayForSection = currentSectionDictionary[#"Top 3"];
return (top3ArrayForSection.count-1 < 1) ? 1 : top3ArrayForSection.count-1;
}
// Cell layout
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Initialize cell
static NSString *CellIdentifier = #"MatchCenterCell";
MatchCenterCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[MatchCenterCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//[cell.contentView addSubview:cell.priceLabel];
[cell.contentView addSubview:cell.conditionLabel];
// No cell seperators = clean design
tableView.separatorColor = [UIColor clearColor];
NSDictionary *currentSectionDictionary = _matchCenterArray[indexPath.section];
NSArray *top3ArrayForSection = currentSectionDictionary[#"Top 3"];
if (top3ArrayForSection.count-1 < 1) {
// title of the item
cell.textLabel.text = #"No items found, but we'll keep a lookout for you!";
cell.textLabel.font = [UIFont systemFontOfSize:12];
}
else {
// title of the item
cell.textLabel.text = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row+1][#"Title"];
cell.textLabel.font = [UIFont systemFontOfSize:14];
// price + condition of the item
NSString *price = [NSString stringWithFormat:#"$%#", _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row+1][#"Price"]];
NSString *condition = [NSString stringWithFormat:#"%#", _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row+1][#"Item Condition"]];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%# - %#", price, condition];
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];
// image of the item
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_matchCenterArray[indexPath.section][#"Top 3"][indexPath.row+1][#"Image URL"]]];
[[cell imageView] setImage:[UIImage imageWithData:imageData]];
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = 2.5;
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == self.expandedSection || indexPath.row <= 3) {
return 65;
}
return 0;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_matchCenterDone == YES) {
self.itemURL = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row+1][#"Item URL"];
[self performSegueWithIdentifier:#"WebViewSegue" sender:self];
}
}
#end
#implementation MoreButton
#end

// Use background thread to avoid the laggy tableView
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Download or get images here
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"url"]];
UIImage *cellImage = [[UIImage alloc] initWithData:imageData];
// Use main thread to update the view. View changes are always handled through main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Refresh image view here
[cell.imageView setImage:cellImage];
[cell.imageView.layer setMasksToBounds:YES];
[cell.imageView.layer setCornerRadius:2.5f];
[cell setNeedsLayout];
});
});

The most common solution to this is AFNetworking's AFImageView. It handles this situation perfectly. It should take you no time at all to implement, so give it a go.

Guy Kogus' answer works great. He's right, I got into all kinds of issues like he mentions in the comment above, doing similar things like the first answer.
Still, here's an example on how to use AFNetworking's UIImageView category. Assuming the code below is in a Cell (or something inheriting from a UIView).
First import the class:
#import "UIImageView+AFNetworking.h"
Then add this code in your UITableViewCell:
NSString *url = #"http://www.domain.www/some_image.jpg";
[self.productImage setImageWithURL:[NSURL URLWithString:url]
placeholderImage:[UIImage imageNamed:#"placeholderImg.png"]];
[self setNeedsLayout];
Not 100% sure if setNeedsLayout is necessary in this case. Feel free to correct this.

Related

How can I tag a button on a UITableView section according to its headerLabel?

I want every delete button to be tagged with its associated section's headerLabel.text.
This way, when pressing the delete button runs the deleteButtonPressed method, the deleteFromMatchCenter Parse function will use the section's headerLabel.text value as the parameter. I've tried to do it as below, but this doesn't seem to be recognizing the header title properly.
How can I properly associate each delete button with its respective sections header title, and send that over as the parameter?
MatchCenterViewController.m:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *matchCenter;
#end
#implementation MatchCenterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewCellStyleSubtitle];
self.matchCenter.frame = CGRectMake(0,50,320,self.view.frame.size.height-100);
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
_matchCenterArray = [[NSArray alloc] init];
}
- (void)viewDidAppear:(BOOL)animated
{
self.matchCenterArray = [[NSArray alloc] init];
[PFCloud callFunctionInBackground:#"MatchCenter"
withParameters:#{
#"test": #"Hi",
}
block:^(NSArray *result, NSError *error) {
if (!error) {
_matchCenterArray = result;
[_matchCenter reloadData];
NSLog(#"Result: '%#'", result);
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _matchCenterArray.count;
}
//the part where i setup sections and the deleting of said sections
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 21.0f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
headerView.backgroundColor = [UIColor lightGrayColor];
_searchTerm = [[[[_matchCenterArray objectAtIndex:section] objectForKey:#"Top 3"] objectAtIndex:3]objectForKey:#"Search Term"];
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 0, 250, 21)];
headerLabel.text = [NSString stringWithFormat:#"%#", _searchTerm];
headerLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
headerLabel.textColor = [UIColor whiteColor];
headerLabel.backgroundColor = [UIColor lightGrayColor];
[headerView addSubview:headerLabel];
UIButton *deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
deleteButton.tag = section;
deleteButton.frame = CGRectMake(300, 2, 17, 17);
[deleteButton setImage:[UIImage imageNamed:#"xbutton.png"] forState:UIControlStateNormal];
[deleteButton addTarget:self action:#selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[headerView addSubview:deleteButton];
return headerView;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Initialize cell
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// No cell seperators = clean design
tableView.separatorColor = [UIColor clearColor];
// title of the item
cell.textLabel.text = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Title"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
// price of the item
cell.detailTextLabel.text = [NSString stringWithFormat:#"$%#", _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Price"]];
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];
// image of the item
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Image URL"]]];
[[cell imageView] setImage:[UIImage imageWithData:imageData]];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 65;
}
- (void)deleteButtonPressed:(id)sender
{
// Define the sections title
NSString *sectionName = [self titleForHeaderInSection:indexPath.section];
// Run delete function with respective section header as parameter
[PFCloud callFunctionInBackground:#"deleteFromMatchCenter"
withParameters:
#{#"searchTerm": sectionName,}
block:^(NSDictionary *result, NSError *error) {
if (!error) {
NSLog(#"Result: '%#'", result);
[_matchCenter reloadData];
}
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
You haven't linked your titleForHeaderInSection: method, but you're trying to pass indexPath.section when you don't have access to your indexPath.
Try this
UIButton *deleteButton = (UIButton *)sender;
NSString *sectionName = [self titleForHeaderInSection:deleteButton.tag];
or
NSString *sectionName = _searchTerm = [[[[_matchCenterArray objectAtIndex:deleteButton.tag] objectForKey:#"Top 3"] objectAtIndex:3]objectForKey:#"Search Term"];

UITableView not properly populating sections with NSArray contents

My Parse cloud code sends back JSON to my iOS app with the following structure:
What I want to do is iterate through this and create a new section in the UITableView for every object in this matchCenterArray.
In this instance, there are three objects in the array, each contains a Top 3 NSDictionary whose value is an array of 3 items, each of which is yet another array of properties. As you can see, I want it set up so that each section has 3 cells, one for each of the top 3 items of that respective matchCenterArray object. I then want it to pull the properties of each item and display it in each cell as the texLabel, detailTextLabel, and thumbnail.
I've tried using a for loop as a solution, but this displays the same item in all cells of the array. This is probably because I'm only looping through matchCenterArray objects, but not additionally looping through items in those objects, as can be seen here:
cell.textLabel.text = [[[[_matchCenterArray objectAtIndex:i] objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Title"];
What I was thinking of doing is nesting a for loop within this one, in place of objectAtIndex:0, but sending a message to a for loop doesn't work.
MatchCenterViewController.m:
#import "MatchCenterViewController.h"
#import <UIKit/UIKit.h>
#interface MatchCenterViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *matchCenter;
#end
#implementation MatchCenterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.matchCenter = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewCellStyleSubtitle];
self.matchCenter.frame = CGRectMake(0,50,320,self.view.frame.size.height-100);
_matchCenter.dataSource = self;
_matchCenter.delegate = self;
[self.view addSubview:self.matchCenter];
_matchCenterArray = [[NSArray alloc] init];
}
- (void)viewDidAppear:(BOOL)animated
{
self.matchCenterArray = [[NSArray alloc] init];
[PFCloud callFunctionInBackground:#"MatchCenterTest"
withParameters:#{
#"test": #"Hi",
}
block:^(NSArray *result, NSError *error) {
if (!error) {
_matchCenterArray = result;
[_matchCenter reloadData];
NSLog(#"Result: '%#'", result);
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _matchCenterArray.count;
}
//the part where i setup sections and the deleting of said sections
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 21.0f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
headerView.backgroundColor = [UIColor lightGrayColor];
// _searchTerm = [[self.matchCenterArray firstObject] objectForKey:#"Search Term"];
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 0, 250, 21)];
// headerLabel.text = [NSString stringWithFormat:#"%#", searchTerm];
// headerLabel.font = [UIFont boldSystemFontOfSize:[UIFont systemFontSize]];
// headerLabel.textColor = [UIColor whiteColor];
headerLabel.backgroundColor = [UIColor lightGrayColor];
[headerView addSubview:headerLabel];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = section + 1000;
button.frame = CGRectMake(300, 2, 17, 17);
[button setImage:[UIImage imageNamed:#"xbutton.png"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[headerView addSubview:button];
return headerView;
}
- (IBAction)deleteButtonPressed:(UIButton *)sender {
NSLog(#"Search Term: '%#'", _searchTerm);
[PFCloud callFunctionInBackground:#"deleteFromMatchCenter"
withParameters:#{
#"searchTerm": _searchTerm,
}
block:^(NSDictionary *result, NSError *error) {
if (!error) {
NSLog(#"Result: '%#'", result);
}
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Initialize cell
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
for (int i = 0; i<[_matchCenterArray count]; i++) {
// populate dictionary with results
//NSDictionary *matchCenterDictionary= [_matchCenterArray objectAtIndex:indexPath.row];
// title of the item
cell.textLabel.text = [[[[_matchCenterArray objectAtIndex:i] objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Title"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
// price of the item
cell.detailTextLabel.text = [NSString stringWithFormat:#"$%#", [[[[_matchCenterArray objectAtIndex:i] objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Price"]];
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];
// image of the item
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[[_matchCenterArray objectAtIndex:i] objectForKey:#"Top 3"] objectAtIndex:0] objectForKey:#"Image URL"]]];
[[cell imageView] setImage:[UIImage imageWithData:imageData]];
//imageView.frame = CGRectMake(45.0,10.0,10,10);
}
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Forget about the loops, the delegate method already run on a loop where the iteration number is equal to the datasource count.
numberOfSectionsInTableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _matchCenterArray.count;
}
numberOfRowsInSection
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{
return _matchCenterArray[section][#"Top 3"].count;
}
cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// Initialize cell
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
// title of the item
cell.textLabel.text = _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Title"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
// price of the item
cell.detailTextLabel.text = [NSString stringWithFormat:#"$%#", _matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Price"];
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];
// image of the item
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:_matchCenterArray[indexPath.section][#"Top 3"][indexPath.row][#"Image URL"]]];
[[cell imageView] setImage:[UIImage imageWithData:imageData]];
return cell;
}
Don't harcode your array count, if your json changes, your code will need to change.
In the future you should look into loading the images asynchronously using a library like SDWebImage in order to avoid lags.
cellForRowAtIndexPath: is called once per numberOfRowsInSection:
You need to add numberOfSections as well:
it should look like this:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Initialize cell
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
// if no cell could be dequeued create a new one
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// the following will set the cell attributes for cells in each section
// to customize per section, use indexPath.section to get which section you are at
// title of the item
cell.textLabel.text = [[[[_matchCenterArray objectAtIndex:indexPath.row objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Title"];
cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
// price of the item
cell.detailTextLabel.text = [NSString stringWithFormat:#"$%#", [[[[_matchCenterArray objectAtIndex:indexPath.row] objectForKey:#"Top 3"] objectAtIndex:0]objectForKey:#"Price"]];
cell.detailTextLabel.textColor = [UIColor colorWithRed:0/255.0f green:127/255.0f blue:31/255.0f alpha:1.0f];
// image of the item
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[[_matchCenterArray objectAtIndex:indexPath.row] objectForKey:#"Top 3"] objectAtIndex:0] objectForKey:#"Image URL"]]];
[[cell imageView] setImage:[UIImage imageWithData:imageData]];
return cell;
}
Please refer to apple's tableView guide

Passing data between views and load a string to Text View

I have a NSMutableArray in a view where I parse some JSON data to a Table View. Now, when I click on a cell I want to pass the value (text) of the selected cell array to a UITextView in another view.
The NSMutableArray where I store the data is called movies.
Here's my code for the first view:
#interface DEMOSecondViewController ()
#end
#implementation DEMOSecondViewController
#synthesize tableView = _tableView, activityIndicatorView = _activityIndicatorView;
#synthesize fontForCellText;
#synthesize btnFaceBook, btnTwitter, btnTwitter2;
#synthesize strURLToLoad;
#synthesize movies;
- (IBAction)showButtonMenu {
[self.frostedViewController presentMenuViewController];
}
- (void)refresh:(UIRefreshControl *)refreshControl {
[refreshControl endRefreshing];
}
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl setTintColor:[UIColor greenColor]];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:refreshControl];
strURLToLoad = [[NSMutableString alloc] init];
[btnFaceBook setTitle:#"link-1" forState:UIControlStateDisabled];
[btnTwitter setTitle:#"link-2" forState:UIControlStateDisabled];
[btnTwitter2 setTitle:#"link-3" forState:UIControlStateDisabled];
[btnFaceBook setBackgroundImage:[UIImage imageNamed:#"tab_selected.png"] forState:UIControlStateNormal];
[btnFaceBook setBackgroundImage:[UIImage imageNamed:#"tab_unselected.png"] forState:UIControlStateSelected];
[btnTwitter setBackgroundImage:[UIImage imageNamed:#"tab_selected.png"] forState:UIControlStateNormal];
[btnTwitter setBackgroundImage:[UIImage imageNamed:#"tab_unselected.png"] forState:UIControlStateSelected];
[btnTwitter2 setBackgroundImage:[UIImage imageNamed:#"tab_selected.png"] forState:UIControlStateNormal];
[btnTwitter2 setBackgroundImage:[UIImage imageNamed:#"tab_unselected.png"] forState:UIControlStateSelected];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PostsObject" owner:self options:nil];
PostsObject *cell = [nib objectAtIndex:0];
fontForCellText = cell.title.font;
cellTextWidth = cell.title.frame.size.width;
cellHeightExceptText = cell.frame.size.height - cell.title.frame.size.height;
[self.navigationController setNavigationBarHidden:YES];
self.tableView.separatorColor = [UIColor clearColor];
self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.activityIndicatorView.color = [UIColor greenColor];
self.activityIndicatorView.hidesWhenStopped = YES;
self.activityIndicatorView.center = self.view.center;
[self.view addSubview:self.activityIndicatorView];
[self.activityIndicatorView startAnimating];
self.tableView.separatorColor = [UIColor clearColor];
movies = [[NSMutableArray alloc] init];
[self btnFromTabBarClicked:btnFaceBook];
}
- (void)loadJSONFromCurrentURL
{
[self.activityIndicatorView startAnimating];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:strURLToLoad]];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[movies setArray:JSON];
[self.activityIndicatorView stopAnimating];
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
}
- (IBAction)btnFromTabBarClicked:(UIButton *)sender
{
btnFaceBook.selected = btnTwitter.selected = btnTwitter2.selected = NO;
sender.selected = YES;
[strURLToLoad setString:[sender titleForState:UIControlStateDisabled]];
[self loadJSONFromCurrentURL];
}
// Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return movies.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row == 0) {
static NSString *Identifier1 = #"TableHeaderView";
TableHeaderView *cell = [tableView dequeueReusableCellWithIdentifier:Identifier1];
if (cell == nil) {
NSArray *nib= [[NSBundle mainBundle] loadNibNamed:#"TableHeaderView" owner:self options:nil];
cell = (TableHeaderView *)[nib objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
} else {
static NSString *Identifier2 = #"PostsObject";
PostsObject *cell = [tableView dequeueReusableCellWithIdentifier:Identifier2];
if (cell == nil) {
NSArray *nib= [[NSBundle mainBundle] loadNibNamed:#"PostsObject" owner:self options:nil];
cell = (PostsObject *)[nib objectAtIndex:0];
cell.backgroundColor = [UIColor clearColor];
}
NSDictionary *movie = [self.movies objectAtIndex:indexPath.row];
NSString *strText = [movie objectForKey:[self getTextKey]];
CGRect rect = cell.title.frame;
rect.size.height = [self getHeightForText:strText];
cell.title.frame = rect;
cell.title.text = strText;
cell.arrow.center = CGPointMake(cell.arrow.frame.origin.x, rect.origin.y + rect.size.height/2);
cell.published.text = [movie objectForKey:[self getPostedTime]];
cell.twitterName.text = [movie objectForKey:[self getTwitterName]];
return cell;
}
return 0;
}
//When I click on the cell, I want the selected cell-text to pass to another view
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//How can I do this?
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 0) {
return 224;
} else {
NSDictionary *movie = [self.movies objectAtIndex:indexPath.row];
NSString *strText = [movie objectForKey:[self getTextKey]];
CGFloat cellHeight = cellHeightExceptText + [self getHeightForText:strText];
return cellHeight;
}
}
- (NSString *)getTextKey
{
return btnTwitter.selected?#"tweet":#"message";
}
- (NSString *)getPostedTime
{
return btnTwitter.selected?#"posted":#"published";
}
- (NSString *)getTwitterName
{
return btnTwitter2.selected?#"user":#"usernew";
}
- (CGFloat)getHeightForText:(NSString *)strText
{
CGSize constraintSize = CGSizeMake(cellTextWidth, MAXFLOAT);
CGSize labelSize = [strText sizeWithFont:fontForCellText constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
NSLog(#"labelSize.height = %f",labelSize.height);
return labelSize.height;
}
#end
And the code so far for the Second View looks like this:
#interface PostsNextView : UIViewController
#property (nonatomic, retain) NSDictionary *theMovie;
#property (nonatomic, retain) IBOutlet UITextView *textView;
#end
So how can I get the value from the first view to pass and display in the Text View in this file?
Add to didSelectRowAtIndexPath code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//get the data you want to pass
NSDictionary *movie = [self.movies objectAtIndex:indexPath.row];
// Allocate second view you want to open
PostsNextView *newView = [[PostsNextView alloc] init];
// pass the data
newView.theMovie = movie;
// present view
[self.navigationController pushViewController:newView animated:YES];
}
And in PostsNextView.m file in viewDidLoad you can show the text you want to show.
//Extended
Add property to keep track which button was pressed:
#interface PostsNextView : UIViewController
#property (nonatomic, retain) NSDictionary *theMovie;
#property (nonatomic, retain) IBOutlet UITextView *textView;
#property int whichButton;
#end
in view did load add this line:
newView.theMovie = movie;
newView.whichButton = _witchBtn;
In your DEMOSecondViewController.m file add ivar:
#implementation DEMOSecondViewController
{
int _witchBtn;
}
And in the same file assign value to _witchBtn for example 0 as a default (all buttons disabled) if you change it to let's say Facebook assign _witchBtn = 1, if twitter _witchBtn = 2, etc.

iOS UITableView content not loading - Property access result unused

I'm trying to load a table with content from Twitter. The table is in a UIView and being created in the drawRect()...but I keep getting a warning:
Property access result unused - getters should not be used for side effects
on each.
Nothing show up in my table.
Here's my .h file:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <Twitter/Twitter.h>
#import "ColorController.h"
#interface TwitterController : UIView <UITableViewDelegate, UITableViewDataSource> {
UIButton* btnCloseView;
UITableView* tblTweets;
UIImageView* imgTwitterIcon;
ColorController* colorManager;
NSMutableArray* tweetsArray;
NSString* twitterID;
}
#property (nonatomic, retain) NSString* twitterID;
- (void) getTweets;
- (void) closeWin;
#end
and my .m
#import "TwitterController.h"
#implementation TwitterController
#synthesize twitterID;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
colorManager = [ColorController new];
}
return self;
}
- (void)drawRect:(CGRect)rect {
imgTwitterIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"imgTwitterBird"]];
CGRect twitterIconFrame = [imgTwitterIcon frame];
twitterIconFrame.origin.x = 50.0;
twitterIconFrame.origin.y = 20.0;
tblTweets = [[UITableView alloc] initWithFrame:CGRectMake(50.0, 25.0, 220.0, 500.0)];
tblTweets.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
tblTweets.separatorColor = [colorManager setColor:176.0:196.0:222.0];
tblTweets.layer.borderWidth = 1.0;
tblTweets.rowHeight = 20.0;
tblTweets.scrollEnabled = YES;
tblTweets.delegate.self;
tblTweets.dataSource.self;
UIImage* imgCloseButton = [UIImage imageNamed:#"btnCloseWindow.png"];
CGSize imageSize = imgCloseButton.size;
btnCloseView = [[UIButton alloc] initWithFrame: CGRectMake(220.0, 550.0, imageSize.width, imageSize.height)];
[btnCloseView setImage:[UIImage imageNamed:#"btnCloseWindow.png"] forState:UIControlStateNormal];
[btnCloseView addTarget:self action:#selector(closeWin:) forControlEvents:UIControlEventTouchUpInside];
[self getTweets];
[self addSubview:tblTweets];
[self addSubview:imgTwitterIcon];
[self addSubview:btnCloseView];
}
- (void) getTweets {
//array to hold tweets
tweetsArray = [[NSMutableArray alloc] init];
///set up a NSURL to the twitter API
NSURL* twitterAPI = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name=%#&count=10", twitterID]];
//get last 10 tweets (max is 20)
TWRequest *twitterRequest = [[TWRequest alloc] initWithURL:twitterAPI
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *tweetsDict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
for(NSDictionary* thisTweetDict in tweetsDict) {
[tweetsArray addObject:[thisTweetDict objectForKey:#"text"]];
}
[tblTweets reloadData];
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
#pragma mark Table Management
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tweetsArray count];
NSLog(#"%i", [tweetsArray count]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tweetsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"tableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.textColor = [UIColor colorWithRed:66.0/255.0 green:66.0/255.0 blue:66.0/255.0 alpha:1];
cell.textLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size: 13.0];
cell.textLabel.text = [tweetsArray objectAtIndex:indexPath.row];
CGRect cellFrame = [cell frame];
cellFrame.size.height = 25.0;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString* thisTweet = [tweetsArray objectAtIndex:indexPath.row];
}
#pragma mark Close Window
- (void) closeWin {
NSMutableDictionary* userData = [[NSMutableDictionary alloc] init];
[userData setObject:#"closeTwitter" forKey:#"theEvent"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"theMessenger" object:self userInfo: userData];
}
#end
drawRect is used to draw stuff inside this views, using drawing functions
You should move your views additions to the layoutSubviews
Instead of - (void)drawRect:(CGRect)rect use - (void)layoutSubviews
This may or may not solve your issues, but nevertheless its the correct approach

GCD and KVO problems

My app is want to get the albums list of the iphone and all the photos in certain album.
In the app I enumerate the photos in one album of the iphone.
As there may be lots of photos of certain album, considering of performance I use GCD:dispatch_async. But it always crashes when the tableview cell updates which is invoked by KVO.
I've no idea about whether I use KVO or GCD in a wrong way.
Now alternatively I use performSelectorInBackground: replacing of dispatch_async. Now the app is not crashed but the app's performance is poor: the title of the cell will only be shown when you touch on it or scroll the tableview when there are many photos. In other words, the main thread must be blocked.
Attached is the code and the core code is in AlbumListViewController.m.
Can any one help me to check it ?
I just want to know:
1 why the app is crashed if using dispatch_async
2 how can I improve the performance in case of many photos.
thanks.
Below is my Code:
//
// RootViewController.h
// AlbumDemo
#import
#interface RootViewController : UITableViewController {
NSMutableArray *_listArray;
}
#property (nonatomic, retain) NSMutableArray *listArray;
#end
// RootViewController.m
#import "RootViewController.h"
#import
#import "AlbumListViewController.h"
NSString *thumnail = #"thumnail";
NSString *albumName = #"albumName";
NSString *albumNum = #"albumNum";
NSString *albumGroup = #"albumGroup";
#implementation RootViewController
#synthesize listArray = _listArray;
#pragma -
#pragma Function
- (void)setUp
{
_listArray = [[NSMutableArray alloc] initWithCapacity:1];
self.title = #"Albums";
}
- (void)fetchAlbumList
{
ALAssetsLibrary *assetLib = [[[ALAssetsLibrary alloc] init] autorelease];
ALAssetsFilter *fileter = [ALAssetsFilter allPhotos];
[assetLib enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
if (group)
{
[group setAssetsFilter:fileter];
NSString *_groupName = [group valueForProperty:ALAssetsGroupPropertyName];
NSNumber *_groupNum = [NSNumber numberWithInteger:[group numberOfAssets]];
UIImage *_groupImage = [UIImage imageWithCGImage:[group posterImage]];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:_groupName,albumName,_groupNum,albumNum,_groupImage,thumnail,group,albumGroup, nil];
[_listArray addObject:dic];
[self.tableView reloadData];
}
else
{
NSLog(#"_listArray :%#",_listArray);
}
}
failureBlock:^(NSError *error)
{
NSLog(#"Error: %#", error);;
}
];
}
#pragma -
#pragma ViewController lift cycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self setUp];
[self fetchAlbumList];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_listArray count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UILabel *nameLab = nil;
UILabel *numLab = nil;
UIImageView *thumnailImage = nil;
UIFont *font = [UIFont boldSystemFontOfSize:18];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
thumnailImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0,50, 50)];
thumnailImage.tag = 100;
[cell.contentView addSubview:thumnailImage];
[thumnailImage release];
nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 100, 30)];
nameLab.tag = 200;
nameLab.backgroundColor = [UIColor clearColor];
nameLab.font = font;
[cell.contentView addSubview:nameLab];
[nameLab release];
numLab = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 50, 30)];
numLab.tag = 300;
numLab.backgroundColor = [UIColor clearColor];
numLab.textColor = [UIColor grayColor];
numLab.font = font;
[cell.contentView addSubview:numLab];
[numLab release];
}
else
{
thumnailImage = (UIImageView *)[cell.contentView viewWithTag:100];
nameLab = (UILabel *)[cell.contentView viewWithTag:200];
numLab = (UILabel *)[cell.contentView viewWithTag:300];
}
NSDictionary *dic = [self.listArray objectAtIndex:indexPath.row];
thumnailImage.image = (UIImage *)[dic valueForKey:thumnail];
NSString *title = [dic valueForKey:albumName];
CGSize titleSize = [title sizeWithFont:font];
CGRect rect = nameLab.frame;
rect.size = titleSize;
nameLab.frame = rect;
nameLab.text = title;
rect = numLab.frame;
rect.origin.x = 60 + nameLab.frame.size.width + 10;
numLab.frame = rect;
numLab.text = [NSString stringWithFormat:#"(%d)",[[dic valueForKey:albumNum] intValue]];
// Configure the cell.
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dic = [self.listArray objectAtIndex:indexPath.row];
AlbumListViewController *viewController = [[AlbumListViewController alloc] initWithAssetGroup:[dic valueForKey:albumGroup]];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc
{
My_Release (_listArray);
[super dealloc];
}
#end
// AlbumListViewController.h
// AlbumDemo
#import
#import
#interface AlbumListViewController : UITableViewController {
NSMutableArray *_marr;
ALAssetsGroup *_assetsGroup;
}
#property (nonatomic, retain) NSMutableArray *list;
#property (nonatomic, retain) ALAssetsGroup *assetsGroup;
- (id)initWithAssetGroup:(ALAssetsGroup *)group;
#end
// AlbumListViewController.m
// AlbumDemo
#import "AlbumListViewController.h"
#interface PhotoObj : NSObject {
NSString *_name;
UIImage *_thumbnail;
UIImage *_fullImage;
}
#property (nonatomic, copy ) NSString *name;
#property (nonatomic, retain) UIImage *thumbnail;
#property (nonatomic, retain) UIImage *fullImage;
#end
#implementation PhotoObj
#synthesize name = _name;
#synthesize thumbnail = _thumbnail,fullImage = _fullImage;
- (void)dealloc
{
My_Release(_thumbnail);
My_Release(_fullImage);
My_Release(_name);
[super dealloc];
}
#end
#interface AlbumListViewController()
- (NSMutableArray*)list;
- (NSUInteger)countOfList;
- (id)objectInListAtIndex:(NSUInteger)idx;
- (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx;
- (id)objectInListAtIndex:(NSUInteger)idx;
- (void)removeObjectFromListAtIndex:(NSUInteger)idx;
- (void)replaceObjectInListAtIndex:(NSUInteger)idx withObject:(id)anObject;
- (void)setList:(NSMutableArray *)_arr;
#end
#implementation AlbumListViewController
#synthesize assetsGroup = _assetsGroup;
- (id)initWithAssetGroup:(ALAssetsGroup *)group
{
self = [self initWithStyle:UITableViewStylePlain];
if (self )
{
_marr = [[NSMutableArray alloc] initWithCapacity:1];
self.assetsGroup = group;
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
My_Release(_marr);
My_Release(_assetsGroup);
[self removeObserver:self forKeyPath:#"list"];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)parseAssetGroup
{
[_marr removeAllObjects];
[self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result)
{
PhotoObj *obj = [[PhotoObj alloc] init];
obj.thumbnail = [UIImage imageWithCGImage:[result thumbnail]];
ALAssetRepresentation *represention = [result defaultRepresentation];
obj.fullImage = [UIImage imageWithCGImage:[represention fullScreenImage]];
obj.name = [[represention url] absoluteString];
[self willChangeValueForKey:#"list"];
[self insertObject:obj inListAtIndex:[_marr count]];
[self didChangeValueForKey:#"list"];
My_Release(obj);
}
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self addObserver:self forKeyPath:#"list" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:NULL];
/*
if performSelectorInBackground, the perofrmance is poor
as the title of the cell will be shown in a long time and it now seems the main thread is blocked
*/
[self performSelectorInBackground:#selector(parseAssetGroup) withObject:nil];
/*
using dispatch_async it always crashes
as it says the sth is wrong with the tableview update
*/
// dispatch_async(dispatch_get_main_queue(), ^{
// [self parseAssetGroup];
// });
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#pragma mark - Table view data source
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
- (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 [_marr count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UIImageView *thumbNail = nil;
UILabel *nameLab = nil;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
thumbNail = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
thumbNail.tag = 99;
[cell.contentView addSubview:thumbNail];
[thumbNail release];
nameLab = [[UILabel alloc] initWithFrame:CGRectMake(60, 10, 240, 40)];
nameLab.numberOfLines = 2;
nameLab.font = [UIFont systemFontOfSize:16];
nameLab.tag = 199;
[cell.contentView addSubview:nameLab];
[nameLab release];
}
else
{
thumbNail = (UIImageView *)[cell.contentView viewWithTag:99];
nameLab = (UILabel *)[cell.contentView viewWithTag:199];
}
// Configure the cell...
PhotoObj *obj = [_marr objectAtIndex:indexPath.row];
nameLab.text = obj.name;
thumbNail.image = obj.thumbnail;
return cell;
}
#pragma mark -
- (NSUInteger)countOfList
{
return [_marr count];
}
- (NSMutableArray*)list
{
return _marr;
}
- (void)setList:(NSMutableArray *)_arr
{
if (_marr != _arr)
{
[_marr release];
_marr = _arr;
}
}
- (id)objectInListAtIndex:(NSUInteger)idx
{
return [_marr objectAtIndex:idx];
}
- (void)insertObject:(id)anObject inListAtIndex:(NSUInteger)idx
{
if ([NSThread isMainThread])
{
NSLog(#"insert main thread");
}
else
{
NSLog(#"insert not main thread");
}
[_marr insertObject:anObject atIndex:idx];
}
- (void)removeObjectFromListAtIndex:(NSUInteger)idx
{
[_marr removeObjectAtIndex:idx];
}
- (void)replaceObjectInListAtIndex:(NSUInteger)idx withObject:(id)anObject
{
[_marr replaceObjectAtIndex:idx withObject:anObject];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSIndexSet *indices = [change objectForKey:NSKeyValueChangeIndexesKey];
if (indices == nil)
return; // Nothing to do
// Build index paths from index sets
NSUInteger indexCount = [indices count];
NSUInteger buffer[indexCount];
[indices getIndexes:buffer maxCount:indexCount inIndexRange:nil];
NSMutableArray *indexPathArray = [NSMutableArray array];
for (int i = 0; i
I ran into exactly the same problem today. In short, the reason is you cannot do UIKit related tasks, like updating a table, or in my case a Textview, from the background dispatch queue. Check the link below for more details.
comparison GCD vs. performSelectorInBackground: dispatch_async not in background
A possible solution is the following: instead of assigning your fresh data in your update block directly to the KVO variable which causes the crash, you dispatch another block which does this to the main queue, from inside your update block. If you use the dispatch_async_f function to do this, you can pass a pointer to your data as context.
Like this:
dispatch_async(yourQueue, ^() {
NSArray *data;
// do stuff to alloc and fill the array
// ...
dispatch_async(dispatch_get_main_queue(), ^() {
myObj.data = data; // the assignment, which triggers the KVO.
});
});
For me this works without retaining and releasing the data. Not sure, if this correct.

Resources