Problem with releasing of object - iphone

I have navigation application with 3 levels of hierarchy. My first level is tabel view controller. The nib file of this controller is "TableView". I have problem here. My code is this:
RootViewController
#import "RootViewController.h"
#import "SubCategory.h"
#import "OffersViewController.h"
#implementation RootViewController
#synthesize subCategories;
#synthesize offersView;
- (void)dealloc
{
NSLog(#"root dealloc");
//[subCategories release];
[super dealloc];
}
- (void)viewDidLoad
{
NSLog(#"root view load");
[super viewDidLoad];
self.title = #"Sub Categories";
NSString *jsonArray = [NSString stringWithFormat:#"{ "
#" \"sub-categories\": { "
#" \"parent\": \"1\", "
#" \"count\": \"2\", "
#" \"sub-category\": [{ "
#" \"id\": \"1\", "
#" \"name\": \"Buy\" "
#" }, "
#" { "
#" \"id\": \"2\", "
#" \"name\": \"Sell\" "
#" }] "
#" } "
#" }"];
SubCategory* categories = [[SubCategory alloc] init];
subCategories = categories;
[subCategories parseJSON:jsonArray];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"root sections");
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"root numberOfRows");
return [subCategories.subCategoryName count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"root didSelectRow");
OffersViewController * offers = [[OffersViewController alloc] initWithNibName:#"OffersView" bundle:nil];
offersView = offers;
// I have exception on this row. exception is:
//-[__NSArrayI objectAtIndex:]: message sent to deallocated instance 0x4b04c00
offersView.title = [NSString stringWithFormat:#"%#", [subCategories.subCategoryName objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:offersView animated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"root cellForRow");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cachedCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] init] autorelease];
}
cell.textLabel.text = [subCategories.subCategoryName objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
#end
SubCategory
#import "SubCategory.h"
#import "JSON.h"
#implementation SubCategory
#synthesize parentId;
#synthesize count;
#synthesize subCategoryId;
#synthesize subCategoryName;
- (void) parseJSON: (NSString*) jsonArray {
NSDictionary *results = [jsonArray JSONValue];
NSString *parent = (NSString*) [[results objectForKey:#"sub-categories"] objectForKey:#"parent"];
parentId = [parent intValue];
NSString *cnt = (NSString*) [[results objectForKey:#"sub-categories"] objectForKey:#"count"];
self.count = [cnt intValue];
NSDictionary *subCategories = [[results objectForKey:#"sub-categories"] objectForKey:#"sub-category"];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithCapacity:[subCategories count]];
NSMutableArray* idArray = [[NSMutableArray alloc] initWithCapacity:[subCategories count]];
for (NSDictionary *subCategory in subCategories) {
[idArray addObject:[subCategory objectForKey:#"id"]];
[namesArray addObject:[subCategory objectForKey:#"name"]];
}
subCategoryId = [NSArray arrayWithArray:idArray];
subCategoryName = [NSArray arrayWithArray:namesArray];
[idArray release];
[namesArray release];
//[parent release];
//[cnt release];
}
#end
I don't know why my object is released. Can someone help me.
EDIT: Added SubCategory code

Here's the problem:
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithCapacity:[subCategories count]];
NSMutableArray* idArray = [[NSMutableArray alloc] initWithCapacity:[subCategories count]];
...
subCategoryId = [NSArray arrayWithArray:idArray];
subCategoryName = [NSArray arrayWithArray:namesArray];
[idArray release];
[namesArray release];
At this point, subCategoryId and subCategoryName are autoreleased. This means they won't be accessible after the parseJSON: method finishes. You have two options:
// Because idArray and namesArray are already retained (you alloc'd them)
subCategoryId = idArray;
subCategoryName = namesArray;
or:
...
// this is equivalent to what you have now, except these two will be retained.
// this is different from the above because subCategoryId and subCategoryName are now NSArrays, whereas above they were NSMutableArrays.
subCategoryId = [idArray copy];
subCategoryName = [namesArray copy];
...

The scope of SubCategory categories is only your viewDidLoad. When you make a shallow copy, it/or the copy will not be available outside your scope, you need to retain it when assigning it to subCategories like
subCategories = [categories retain];
Also, you can forego using the SubCategory categories at all. Instead do:
subCategories = [[SubCategory alloc] init];
[subCategories parseJSON:jsonArray]; }

Related

Xcode live news feed with multiple accounts or user

Im new to xcode.
and Im creating this facebook, twitter, instagram and youtube news feed in a tableview
at first Im trying this facebook to load multiple json
#import "ViewController.h"
#import "DetailViewController.h"
#import <SDWebImage/UIImageView+WebCache.h>
#define kQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define kJSONUrl [NSURL URLWithString:#"https://www.facebook.com/feeds/page.php?id=1387524391527078&format=json"]
#define kJSONUrl1 [NSURL URLWithString:#"https://www.facebook.com/feeds/page.php?id=196934133652011&format=json"]
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#interface ViewController ()
#end
#implementation ViewController
#synthesize jsonTitles,table,jsonContent;
-(void)loadJSON:(NSData *)responseData {
NSError *error;
NSArray *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSArray *array = [json valueForKey:#"entries"];
for (int i = 0; i < array.count; i++) {
NSArray *entry = [array objectAtIndex:i];
NSString *title = [entry valueForKey:#"title"];
NSString *content = [entry valueForKey:#"content"];
[jsonTitles addObject:title];
[jsonContent addObject:content];
}
[self.table reloadData];
}
- (void)viewDidLoad
{
self.table.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
jsonTitles = [[NSMutableArray alloc]init];
jsonContent = [[NSMutableArray alloc]init];
dispatch_async(kQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:kJSONUrl];
[self performSelectorOnMainThread:#selector(loadJSON:) withObject:data waitUntilDone:YES];
NSData *data1 = [NSData dataWithContentsOfURL:kJSONUrl1];
[self performSelectorOnMainThread:#selector(loadJSON:) withObject:data1 waitUntilDone:YES];
});
[super viewDidLoad];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 40;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [jsonTitles count];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"detailSegue" sender:nil];
// NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:#"detailSegue" ascending:YES];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"detailSegue"]) {
DetailViewController *detail = segue.destinationViewController;
NSIndexPath *path = [self.table indexPathForSelectedRow];
detail.titleText = [jsonTitles objectAtIndex:path.row];
detail.contentText = [jsonContent objectAtIndex:path.row];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *tableCellID = #"tableCellID";
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableCellID];
UITableViewCell *cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:tableCellID];
NSString *myurl = #"http://graph.facebook.com/idoltap/picture";
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:myurl]];
UIImage *myimage = [[UIImage alloc] initWithData:imageData];
cell.imageView.image = myimage;
// cell.imageView.image = [UIImage imageNamed:#"background.png"];
cell.textLabel.text = #"iDOLTap";
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tableCellID];
}
cell.detailTextLabel.text = [jsonTitles objectAtIndex:indexPath.row];
if ([cell.detailTextLabel.text isEqual: #" "]) {
cell.detailTextLabel.text = [jsonContent objectAtIndex:indexPath.row];
}
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Is my pattern correct?
I dont know how I can load the right profile picture for each json/user and sort it by its published date.
I just want some help to fix this one in the right way.

UISearch not showing results

I'm querying from parse and I when I try to search using the search bar it returns with "no results." I'm trying to figure out what I did wrong showing the objects because it does show in the NSLog that it has found objects.
My viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.tableView.tableHeaderView = self.searchBar;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
self.searchResults = [NSMutableArray array];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
The Retrieving for the Table View:
-(void)filterResults:(NSString *)searchTerm {
[self.searchResults removeAllObjects];
PFQuery *query = [PFQuery queryWithClassName:#"New"];
[query whereKeyExists:#"title"]; //this is based on whatever query you are trying to accomplish
[query whereKey:#"title" containsString:searchTerm];
NSArray *results = [query findObjects];
NSLog(#"%u", results.count);
[self.searchResults addObjectsFromArray:results];
}
Finally, The tableview cell that doesn't want to work:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFObject * testObject = [self.searchResults objectAtIndex:indexPath.row];
cell.textLabel.text = [testObject objectForKey:#"title"];
return cell;
}
Then:
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterResults:searchString];
return YES;
}
Then to load from Parse to UTTableview:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithClassName:#"New"];
self = [super initWithCoder:aDecoder];
if (self) {
// The className to query on
self.ClassName = #"New";
//self.textKey = #"title";
// The key of the PFObject to display in the label of the default cell style
// self.keyToDisplay = #"text";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = NO;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
self.loadingViewEnabled = NO;
// The number of objects to show per page
self.objectsPerPage = 50;
}
return self;
}
You can use another way where you can take the help of the search bar delegate method.
e.g - Include UISearchBarDelegate in your header file and also -
#property (strong, nonatomic) IBOutlet UISearchBar *searchSong;
Now in your .m file
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
NSMutableArray *temp = [[NSMutableArray alloc] init];
temp = [self searchSongsData:self.searchSong.text from:`YourArray`];
}
+ (NSMutableArray *) searchSongsData:(NSString *)searchtext from:(NSMutableArray *)array
{
NSMutableArray *arrayToBeReturned = [[NSMutableArray alloc] init];
NSArray *temp = [[NSArray alloc] initWithArray:array];
NSString *textToBeSearch = [searchtext lowercaseString];
SongsMoviesData *songs;
for(int i = 0; i<temp.count; i++)
{
songs = [temp objectAtIndex:i];
NSString *string1 = [songs.song_title lowercaseString];
NSString *string2 = [songs.moview_name lowercaseString];
NSString *string3 = [songs.singer_name lowercaseString];
if ([string1 rangeOfString:textToBeSearch].location != NSNotFound ||[string2 rangeOfString:textToBeSearch].location != NSNotFound || [string3 rangeOfString:textToBeSearch].location != NSNotFound)
{
// NSLog(#"%#", songs.moview_name);
[arrayToBeReturned addObject:songs];
}
}
return arrayToBeReturned;
}
and like this you save this array in your search results.

UITableView - methods are not being called

I have a subview in which I build a UITableview, I've set the delegate and datasource to the subview but for some reason the table methods are not being called...can someone look over my code and see what I am doing wrong? Thanks
.h file
#interface TwitterController : UIView <UITableViewDelegate, UITableViewDataSource> {
UIButton* btnCloseView;
UITableView* tblTweets;
UIImageView* imgTwitterIcon;
ColorController* colorManager;
NSMutableArray* tweetsArray;
NSMutableArray* tableData;
NSString* twitterID;
}
#property (nonatomic, retain) NSString* twitterID;
- (NSMutableArray* ) getTweets;
- (void) sendNotification : (id) sender;
#end
.m file
#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 {
}*/
- (void)layoutSubviews {
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;
tableData = [self getTweets];
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(sendNotification:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:tblTweets];
[self addSubview:imgTwitterIcon];
[self addSubview:btnCloseView];
}
- (NSMutableArray*) getTweets {
//array to hold tweets
tweetsArray = [[NSMutableArray alloc] init];
twitterID = #"Pruit_Igoe";
///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"]];
}
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
return tweetsArray;
}
#pragma mark Table Management
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tableData count];
NSLog(#"%i", [tableData count]); //does not log!
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tableData 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 = [tableData objectAtIndex:indexPath.row];
}
#pragma mark Close Window
- (void) sendNotification : (id) sender {
NSMutableDictionary* userData = [[NSMutableDictionary alloc] init];
[userData setObject:#"closeTwitter" forKey:#"theEvent"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"theMessenger" object:self userInfo: userData];
}
#end
I think you are not allocate and initialize tableData. Write it tableData = [[NSMutableArray alloc] init]; in - (id)initWithFrame:(CGRect)frame method or - (void)layoutSubviews method. Just try it.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"%i", [tableData count]);
return [tableData count];
//NSLog(#"%i", [tableData count]); //does not log!
}
If the output is zero, tableData array is empty. Check it tableData array
You should do all the initialization (tblTweets for example) in initWithFrame:.
layoutSubviews is ment for laying out subviews.
In fact your code will (should) work if you move all the code fromLayoutSubviews to initWithFrame:. You can then move parts of the code (the laying out part) back.
EDIT: when you move initializing code you will probably also have to add [tblTweets reloadData]; right after tableData = [self getTweets];

Obj-C, iOS, How do I sort by value and not key, sortedArrayUsingSelector, currently #selector(compare:)]

I need to sort by value instead of Key, I think....
Heres where I populate my arrarys
const char *sql = "select cid, category from Categories ORDER BY category DESC";
sqlite3_stmt *statementTMP;
int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) {
while(sqlite3_step(statementTMP) == SQLITE_ROW)
{
int cid = sqlite3_column_int(statementTMP, 0);
NSString *category = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statementTMP, 1)];
NSArray *arr=[[NSArray alloc]initWithObjects:category,nil];
[arrayTmp setObject:arr forKey:[NSString stringWithFormat:#"%i",cid]];
[self.cidList addObject:[NSString stringWithFormat:#"%i",cid]];
[category release];
[arr release];
}
}
sqlite3_finalize(statementTMP);
sqlite3_close(database);
self.allCategories = arrayTmp;
[arrayTmp release];
Heres the method where the arrays are re-sorted.
- (void)resetSearch {
NSMutableDictionary *allCategoriesCopy = [self.allCategories mutableDeepCopy];
self.Categories = allCategoriesCopy;
[allCategoriesCopy release];
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObject:UITableViewIndexSearch];
[keyArray addObjectsFromArray:[[self.allCategories allKeys]
sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;
[keyArray release];
}
This is a problem i've had for some time, last time I looked at this I could find an altervative to sortedArrayUsingSelector compare?
EDIT
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [Categories objectForKey:key];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SectionsTableIdentifier ];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier: SectionsTableIdentifier ] autorelease];
}
cell.textLabel.text = [nameSection objectAtIndex:row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [Categories objectForKey:key];
NSLog(#"the selected cid is = %i",[key intValue]);
selectButton.enabled = YES;
}
Anyone?
Your obviously attempting to construct an array for use in the -[UITableviewDatasource sectionIndexTitlesForTableView:]. As such, you need an array that looks like this (pseudo-code):
[UITableViewIndexSearch, 0_sectionTitle, 1_sectionTitle, 2_sectionTitle, ...]
I think your immediate problem is that you try to add the UITableViewIndexSearch string constant to the array before you sort which makes it impossible for it end up as the first element unless all your other elements sort below U.
The fix is simple, just add the constant after the sort. You can clean the code up while you're at it:
NSMutableArray *secIdx=[NSMutableArray arrayWithCapacity:[[self.allCategories allKeys] count]];
[secIdx addObjectsFromArray:[self.allCategories allKeys]];
[secIdx sortUsingSelector:#selector(compare:)];
[secIdx insertObject:UITableViewIndexSearch atIndex:0];
self.keys=secIdx;
Note that secIdx is autoreleased so you don't have to release it.
Aside from this problem, your code has a lot of unnecessary/dangerous elements that will make your app fragile and hard to maintain.
You are using a lot of init for objects that you could use autoreleased convenience methods for. The 'init`s poise the risk of memory leaks but give you no advantage.
You need to wrap scalar values in objects so they can be easily managed in collections.
You are using an unnecessary array.
You can rewrite the first block like so:
const char *sql = "select cid, category from Categories ORDER BY category DESC";
sqlite3_stmt *statementTMP;
int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) {
NSNumber *cidNum; //... move variable declerations outside of loop
NSString *category; //.. so they are not continously recreated
[self.allCategories removeAllObjects]; //... clears the mutable dictionary instead of replacing it
while(sqlite3_step(statementTMP) == SQLITE_ROW){
cidNum=[NSNumber numberWithInt:(sqlite3_column_int(statementTMP, 0))];
category=[NSString stringWithUTF8String:(char *)sqlite3_column_text(statementTMP, 1)];
//... adding the autoreleased category and cidNum to array/dictionary automatically retains them
[self.allCategories addObject:category forKey:cidNum];
[self.cidList addObject:cidNum];
//[category release]; ... no longer needed
//[arr release]; ... no longer needed
}
}
sqlite3_finalize(statementTMP);
sqlite3_close(database);
//self.allCategories = arrayTmp; ... no longer needed
//[arrayTmp release]; ... no longer needed
Use -sortedArrayUsingComparator: (or -sortedArrayUsingFunction:context: if you can't use blocks). Example:
NSDictionary *categories = [self allCategories];
NSArray *keysSortedByValue = [[categories allKeys] sortedArrayUsingComparator:
^(id left, id right) {
id lval = [categories objectForKey:left];
id rval = [categories objectForKey:right];
return [lval compare:rval];
}];
You could make a small model class Category and implement compare inside of it, then sort an array of those objects using that compare:.
Here's some info - How to sort an NSMutableArray with custom objects in it?
Perhaps you're looking for NSSortDescriptor (and the corresponding sort method, -[NSArray sortedArrayUsingDescriptors]) and friends?
If I understood correctly then what you wish to do to get categories from database & display it on a tableView with alphabetical sorting, index on right & search bar on top. Ideally, you would like to display the Contacts application kind of a view. If that's correct, use below code for fetching items from DB & rebuilding (or resetting) it -
const char *sql = "select cid, category from Categories ORDER BY category DESC";
sqlite3_stmt *statementTMP;
NSMutableArray *arrayTmp = [[NSMutableArray alloc] init];
int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) {
while(sqlite3_step(statementTMP) == SQLITE_ROW) {
int cid = sqlite3_column_int(statementTMP, 0);
NSString *category = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statementTMP, 1)];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:category forKey:#"Category"];
[dict setObject:[NSNumber numberWithInt:cid] forKey:#"CID"];
[arrayTmp addObject:dict];
[dict release];
[category release];
}
}
sqlite3_finalize(statementTMP);
sqlite3_close(database);
self.allCategories = arrayTmp;
[arrayTmp release];
And then rebuild the items using this function -
- (void)rebuildItems {
NSMutableDictionary *map = [NSMutableDictionary dictionary];
for (int i = 0; i < allCategories.count; i++) {
NSString *name = [[allCategories objectAtIndex:i] objectForKey:#"Category"];
NSString *letter = [name substringToIndex:1];
letter = [letter uppercaseString];
if (isdigit([letter characterAtIndex:0]))
letter = #"#";
NSMutableArray *section = [map objectForKey:letter];
if (!section) {
section = [NSMutableArray array];
[map setObject:section forKey:letter];
}
[section addObject:[allCategories objectAtIndex:i]];
}
[_items release];
_items = [[NSMutableArray alloc] init];
[_sections release];
_sections = [[NSMutableArray alloc] init];
NSArray* letters = [map.allKeys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
for (NSString* letter in letters) {
NSArray* items = [map objectForKey:letter];
[_sections addObject:letter];
[_items addObject:items];
}
}
Now, displaying items in tableView, use below methods -
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
if (_sections.count)
return _sections.count;
else
return 1;
}
- (NSInteger)tableView:(UITableView*)tableView sectionForSectionIndexTitle:(NSString *)title
atIndex:(NSInteger)index {
if (tableView.tableHeaderView) {
if (index == 0) {
[tableView scrollRectToVisible:tableView.tableHeaderView.bounds animated:NO];
return -1;
}
}
NSString* letter = [title substringToIndex:1];
NSInteger sectionCount = [tableView numberOfSections];
for (NSInteger i = 0; i < sectionCount; i++) {
NSString* section = [tableView.dataSource tableView:tableView titleForHeaderInSection:i];
if ([section hasPrefix:letter]) {
return i;
}
}
if (index >= sectionCount) {
return sectionCount-1;
} else {
return index;
}
}
- (NSArray*)lettersForSectionsWithSearch:(BOOL)withSearch withCount:(BOOL)withCount {
if (isSearching)
return nil;
if (_sections.count) {
NSMutableArray* titles = [NSMutableArray array];
if (withSearch) {
[titles addObject:UITableViewIndexSearch];
}
for (NSString* label in _sections) {
if (label.length) {
NSString* letter = [label substringToIndex:1];
[titles addObject:letter];
}
}
if (withCount) {
[titles addObject:#"#"];
}
return titles;
} else {
return nil;
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [self lettersForSectionsWithSearch:YES withCount:NO];
}
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
if (_sections.count) {
NSArray* items = [_items objectAtIndex:section];
return items.count;
} else {
return _items.count;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (_sections.count)
return [_sections objectAtIndex:section];
return nil;
}
- (id)tableView:(UITableView *)tableView objectForRowAtIndexPath:(NSIndexPath *)indexPath {
if (_sections.count) {
NSArray *section = [_items objectAtIndex:indexPath.section];
return [section objectAtIndex:indexPath.row];
} else {
return [_items objectAtIndex:indexPath.row];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Create your UITableViewCell.
// Configure the cell.
NSDictionary *dict = [self tableView:tableView objectForRowAtIndexPath:indexPath];
cell.textLabel.text = [dict objectForKey:#"Category"];
cell.detailTextLabel.text = [NSString stringWithFormat:%d, [[dict objectForKey:#"CID"] intValue]];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if (isSearching)
return nil;
NSString *title = #"";
if (_sections.count) {
title = [[_sections objectAtIndex:section] substringToIndex:1];
} else {
return nil;
}
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 20)];
view.backgroundColor = [UIColor colorWithRed:(58/255.0) green:(27/255.0) blue:(6/255.0) alpha:1.0];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 1, 50, 18)];
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:17.0];
label.text = title;
[view addSubview:label];
[label release];
return [view autorelease];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *dict = [self tableView:tableView objectForRowAtIndexPath:indexPath];
NSLog(#"selected row id:%d, name:%#", [dict objectForKey:#"Category"], [[dict objectForKey:#"CID"] intValue]);
}
The rest part is implementing the UISearchBarDelegate and implementing searching of tableView which can be done using below code:
- (void)searchBar:(UISearchBar *)searchbar textDidChange:(NSString *)searchText {
[_sections removeAllObjects];
[_items removeAllObjects];
if([searchText isEqualToString:#""] || searchText == nil) {
[self rebuildItems];
return;
}
NSInteger counter = 0;
for(NSDictionary *dict in allCategories) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRange r = [[dict objectForKey:#"Category"] rangeOfString:searchText options:NSCaseInsensitiveSearch];
if(r.location != NSNotFound) {
if(r.location == 0) {
[_items addObject:dict];
}
}
counter++;
[pool release];
}
[contactList reloadData];
}
Hope this is what you're looking for.
On your sorting function u should try this:
NSArray *cntxt; //im not sure this is the correct type that ur using on keyArray
[keyArray addObjectsFromArray:[self.allCategories allKeys]];
[keyArray sortUsingFunction:compareFunction context:cntxt];
And the compare function you modify to your needs
NSInteger compareFunction(id x, id y, void *context) {
//NSArray *ctxt = context;
NSArray *c1 = x;
NSArray *c2 = y;
if ([c1 value] < [c2 value])
return NSOrderedDescending;
else if ([c1 value] > [c2 value])
return NSOrderedAscending;
else
return NSOrderedSame;
}
Edit: After reading your comments and after relooking at your code, it seems like that your keyArray as objects of the type NSString, so you should change:
NSInteger compareFunction(id x, id y, void *context) {
//NSString *ctxt = context;
NSString *c1 = x;
NSString *c2 = y;
NSComparisonResult result;
result = [c1 compare:c2];
if (result<0)
return NSOrderedAscending;
else if (result>0)
return NSOrderedDescending;
else
return NSOrderedSame;
}

UISearchBar in iPhone/iPad application

I have data like this...(All the data comes from .plist file...)
Searching Array - (
{
FirstName = "Ramesh";
LastName = "Bean";
EmpCode = 1001;
},
{
FirstName = "Rohan";
LastName = "Rathor";
EmpCode = 102;
},
{
FirstName = "Priya";
LastName = "Malhotra";
EmpCode = 103;
},
{
FirstName = "Mukesh";
LastName = "Sen";
EmpCode = 104;
},
{
FirstName = "Priya";
LastName = "Datta";
EmpCode = 105;
}
)
I want implement search data from this array on the basis of FirstName (key).
I am able to search data with the "FirstName(Key)"
but after filtering data suppose i clicked Row( in the data) which is displayed in the TableView. It Navigate me to New-Controller with all the information of that particular employee (like: FirstName,LastName,EmpCode).
How can i get information?
As i gone through the search sample codes.
Here is my search code...
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
NSInteger TotalNoOfRecords=[self.SearchtableDataSource count];
for (int i=0;i<TotalNoOfRecords;i++)
{ NSDictionary *dictionary = [self.SearchtableDataSource objectAtIndex:i];
NSArray *array = [dictionary objectForKey:#"FirstName"];
[searchArray addObject:array];
}
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
[copyListOfItems addObject:sTemp];
}
}
How can i improve this code?....Please guide me... [searchArray release]; searchArray = nil;
How we maintain all the "Keys(FirstName,LastName,EmpCode)" in the searchArray please help me out? Thanks...
I think the best approach would be to make an array of NSDictionary which has value for three keys namely "FirstName", "LastName", "EmpCode"
now to filter the data according to "FirstName" use NSPredicate instead of for loop,
NSString *searchText = searchBar.text;
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"FirstName like[cd] %#",searchText];
NSArray* filteredArray = [self.SearchtableDataSource filteredArrayUsingPredicate:predicate];
In method cellForRowAtIndexPath
NSDictionary* currentEmp = [filteredArray objectAtIndex:inddexPath.row];
display the information in this currentEmp.
Similarly in didSelectRowAtIndexPath
NSDictionary* currentEmp = [filteredArray objectAtIndex:inddexPath.row];
and pass this dictionary in the next ViewController in which u want to dispaly the detail of the current employ.
if u have two table views one that of searchDisplayController (showing the filtered result)
and one which is showing the whole result, then u can track this by having a BOOL variable tacking that is if filtering is active or not.
Here is what i have done and its working fine with me(tested it). I have taken one IBOutlet of UISearchBar so here is the code for ur .h file
#interface SearchForEmp : UIViewController<'UISearchDisplayDelegate,UISearchBarDelegate,UITableViewDelegate,UITableViewDataSource> {
IBOutlet UISearchBar* mySearchBar;
UISearchDisplayController* mySearchDisplayController;
NSMutableArray* allEmp;
NSMutableArray* filteredEmp;
BOOL isFilatering;
}
#end
Now in .m file
-(void)viewDidLoad
{
[super viewDidLoad];
mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:mySearchBar contentsController:self];
mySearchDisplayController.searchResultsDelegate = self;
mySearchDisplayController.searchResultsDataSource = self;
mySearchDisplayController.delegate = self;
isFilatering = NO;
filteredEmp = [[NSMutableArray alloc] init];
allEmp = [[NSMutableArray alloc] init];
NSDictionary *temp;
for (int i = 0; i < 30; i++) {
temp = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:#"FirstName_%d",i],#"FirstName",[NSString stringWithFormat:#"LastName_%d",i],#"LastName",[NSNumber numberWithInt:1000+i],#"EmpCode",nil];
[allEmp addObject:temp];
}
}
for e.g purpose i have thake an array of 30 Dictionaries, each with a unique name.For table view data source and delegate .....
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isFilatering) {
return [filteredEmp count];
}
else {
return [allEmp count];
}
return [allEmp count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] init] autorelease];
}
NSDictionary* temp;
if (isFilatering) {
temp = [filteredEmp objectAtIndex:indexPath.row];
}
else {
temp = [allEmp objectAtIndex:indexPath.row];
}
cell.textLabel.text = [NSString stringWithFormat:#"%# %#",[temp objectForKey:#"FirstName"],[temp objectForKey:#"LastName"]];
return cell;
}
Now as far as searching is concerned here is what u need to do .......
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSPredicate* predicate = [[NSPredicate predicateWithFormat:#"self.FirstName contains %#",mySearchBar.text] retain];
filteredEmp = [[allEmp filteredArrayUsingPredicate:predicate] retain];
}
-(void) searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
isFilatering = YES;
}
-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
isFilatering = NO;
}
I hope that helps you understanding the things, dont forget to add retain in line
filteredEmp = [[allEmp filteredArrayUsingPredicate:predicate] retain];
because method filteredArrayUsingPredicate: is an accessor method and its retain count is handled by the NSArray so have access to the filtered array u need to pass a retain msg to it.