sorting the array in table view in iOS 6 - iphone

Hello I have created one of my application in which i have implemented sorting functionality in a table view The sorting method is working fine on iOS 4 and 5 but when I try to test the application on iOS 6, it shows an error in the sorting method on iOS 6
Please help
Method :-
-(void)setupIndexData{
self.arrayOfCharacters =[[NSMutableArray alloc]init];
self.objectForCharacter=[[NSMutableDictionary alloc]init];
NSNumberFormatter *formatter =[[NSNumberFormatter alloc]init];
NSMutableArray *arrayOfNames =[[NSMutableArray alloc]init];
NSString *numbericSection = #"#";
NSString *firstLetter;
for (NSDictionary *item in self.mCompanyarray) {
firstLetter = [[[item valueForKey:#"Company"]description] substringToIndex:1];
// Check if it's NOT a number
if ([formatter numberFromString:firstLetter] == nil) {
/**
* If the letter doesn't exist in the dictionary go ahead and add it the
* dictionary.
*
* ::IMPORTANT::
* You HAVE to removeAllObjects from the arrayOfNames or you will have an N + 1
* problem. Let's say that start with the A's, well once you hit the
* B's then in your table you will the A's and B's for the B's section. Once
* you hit the C's you will all the A's, B's, and C's, etc.
*/
if (![objectForCharacter objectForKey:firstLetter]) {
[arrayOfNames removeAllObjects];
[arrayOfCharacters addObject:firstLetter];
}
[arrayOfNames addObject:item];
/**
* Need to autorelease the copy to preven potential leak. Even though the
* arrayOfNames is released below it still has a retain count of +1
*/
[objectForCharacter setObject:[[arrayOfNames copy] autorelease] forKey:firstLetter];
} else {
if (![objectForCharacter objectForKey:numbericSection]) {
[arrayOfNames removeAllObjects];
[arrayOfCharacters addObject:numbericSection];
}
[arrayOfNames addObject:item];
[objectForCharacter setObject:[[arrayOfNames copy] autorelease] forKey:numbericSection];
}
}
[formatter release];
[arrayOfNames release];
[self.mCompaniesTableView reloadData];
}
Thanks

I'd use UILocalizedIndexedCollation to sort and index your data. That way, your app can support multiple languages etc.
Note: I haven't tested the code below, but the theory is there.
First, create a #property to store the indexed data:
#property (nonatomic, strong) NSDictionary *indexedSections;
Set up your table like this:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//we use sectionTitles and not sections
return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.indexedSections objectForKey:[NSNumber numberWithInteger:section]] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
BOOL showSection = [[self.indexedSections objectForKey:[NSNumber numberWithInteger:section] count] != 0;
//only show the section title if there are rows in the section
return (showSection) ? [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:section] : nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [[self.indexedSections objectForKey:[NSNumber numberWithInteger:indexPath.section]] objectAtIndex:indexPath.row];
// configure the cell
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
//sectionForSectionIndexTitleAtIndex: is a bit buggy, but is still useable
return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index];
}
And finally index like this:
- (void) setupIndexData
{
// asynchronously sort
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
// create a dictionary to store an array of objects for each section
NSMutableDictionary *tempSections = [NSMutableDictionary dictionary];
// iterate through each dictionaey in the list, and put them into the correct section
for (NSDictionary *item in self.mCompanyarray)
{
// get the index of the section (Assuming the table index is showing A-#)
NSInteger indexName = [[UILocalizedIndexedCollation currentCollation] sectionForObject:[item valueForKey:#"Company"] collationStringSelector:#selector(description)];
NSNumber *keyName = [NSNumber numberWithInteger:indexName];
// if an array doesnt exist for the key, create one
NSMutableArray *arrayName = [tempSections objectForKey:keyName];
if (arrayName == nil)
{
arrayName = [NSMutableArray array];
}
// add the dictionary to the array (add the actual value as we need this object to sort the array later)
[arrayName addObject:[item valueForKey:#"Company"]];
// put the array with new object in, back into the dictionary for the correct key
[tempSections setObject:arrayName forKey:keyName];
}
/* now to do the sorting of each index */
NSMutableDictionary *sortedSections = [NSMutableDictionary dictionary];
// sort each index array (A..Z)
[tempSections enumerateKeysAndObjectsUsingBlock:^(id key, id array, BOOL *stop)
{
// sort the array - again, we need to tell it which selctor to sort each object by
NSArray *sortedArray = [[UILocalizedIndexedCollation currentCollation] sortedArrayFromArray:array collationStringSelector:#selector(description)];
[sortedSections setObject:[NSMutableArray arrayWithArray:sortedArray] forKey:key];
}];
// set the global sectioned dictionary
self.indexedSections = sortedSections;
dispatch_async(dispatch_get_main_queue() ,^{
// reload the table view (on the main thread)
[self.mCompaniesTableView reloadData];
});
});
}

Related

how to populate more tableview sections with one array

I'm working on a tableview that displays data about a company. I want to divide the data over 3 sections, to make it look more organized.
The data about a company is retreived from a mysql database and I receive it in one array, which looks like this:
{
companyAdress = "the street 9";
companyCity = "city";
companyFacebook = "facebook.com/companyname";
companyName = "name";
companyPhoneNumber = "0123 456 789";
companyTwitter = "www.twitter.com/companyname";
companyWebsite = "www.companyname.com";
companyZip = "0000 AA";
imageNumber = "3067913";
}
I want the companyName and imageNumber in the first section, the companyAdress, companyZip and companyCity in the second, and all the remaining variables in the third section.
I do not know how to properly do this, and I haven't found a useful answer/solution for this on SO or any other website I know.
How to I do this? any help, sample code and/or tutorial would be much appreciated, thank you in advance!
One approach would be to separate the data when you receive it into a two-dimensional array. So the array's first entry would be an array holding companyName and imageNumber, and so on.
With this implementation, numberOfSectionsInTableView would simply return myArray.count and numberOfRowsInSection would return myArray[section].count.
To access the appropriate values from there, you would do something like ((NSMutableArray*)myArray[indexpath.section])[indexpath.row]
You must use a Array of NSDictionary items,
then you get info for sections and tables rows.
adding a key for each record type.
This is a sample project for explain the use of NSArray and NSDictonary, I hope this help you.
You can download the xcode project from here http://www.germinara.it/download/FGTestTableView.zip and this is the result of the sample http://www.germinara.it/download/FGtesttableview.png
#import <UIKit/UIKit.h>
#interface FGViewController : UIViewController <UITableViewDataSource,UITableViewDelegate> {
NSMutableArray* records;
}
#property(nonatomic,strong) IBOutlet UITableView *tblRecordsList;
-(void) buildDataSource; //Build the datasource for the tableview
#end
#import "FGViewController.h"
#interface FGViewController ()
#end
#implementation FGViewController
#synthesize tblRecordsList;
- (void)viewDidLoad
{
[super viewDidLoad];
records = [[NSMutableArray alloc] init];
//Load data into array used as datasource
[self buildDataSource];
self.tblRecordsList.dataSource=self;
self.tblRecordsList.delegate=self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//Load sample data
-(void) buildDataSource{
NSMutableDictionary* dict= nil;
[records removeAllObjects];
//Fill data source with your data
//Data to put on first section
dict=[NSMutableDictionary dictionaryWithCapacity:0];
[dict setObject:#"0" forKey:#"idsection"];
[dict setObject:#"company1" forKey:#"companyName"];
[dict setObject:#"picture1" forKey:#"imageNumber"];
[records addObject:dict]; //Add items to array
//Data to put on second section
dict=[NSMutableDictionary dictionaryWithCapacity:0];
[dict setObject:#"1" forKey:#"idsection"];
[dict setObject:#"address1" forKey:#"companyAdress"];
[dict setObject:#"zip1" forKey:#"companyZip"];
[dict setObject:#"city1" forKey:#"companyCity"];
[records addObject:dict]; //Add items to array
//Data to put on other section
dict=[NSMutableDictionary dictionaryWithCapacity:0];
[dict setObject:#"2" forKey:#"idsection"];
[dict setObject:#"facebook1" forKey:#"companyFacebook"];
[dict setObject:#"phone1" forKey:#"companyPhoneNumber"];
[dict setObject:#"twitter1" forKey:#"companyTwitter"];
[dict setObject:#"website1" forKey:#"companyWebsite"];
[records addObject:dict]; //Add items to array
}
//Get Dictionary using section key (idsection)
-(NSDictionary *) dictionaryForSection:(NSInteger) section{
for (NSDictionary *dict in records){
if(section == [[dict valueForKey:#"idsection"] intValue]){
return dict;
}
}
return nil;
}
//Table View Delegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell =nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"myCellReuseID"];
NSDictionary * dict = [self dictionaryForSection:indexPath.section]; //Get request dictionary info
//Process data for first section
if(indexPath.section == 0){
if(indexPath.row == 0)
cell.textLabel.text=[dict valueForKey:#"companyName"];
if(indexPath.row == 1)
cell.textLabel.text=[dict valueForKey:#"imageNumber"];
}
//Process data for second section
if(indexPath.section == 1){
if(indexPath.row == 0)
cell.textLabel.text=[dict valueForKey:#"companyAdress"];
if(indexPath.row == 1)
cell.textLabel.text=[dict valueForKey:#"companyZip"];
if(indexPath.row == 2)
cell.textLabel.text=[dict valueForKey:#"companyCity"];
}
//Process data for other section
if(indexPath.section == 2){
if(indexPath.row == 0)
cell.textLabel.text=[dict valueForKey:#"companyFacebook"];
if(indexPath.row == 1)
cell.textLabel.text=[dict valueForKey:#"companyPhoneNumber"];
if(indexPath.row == 2)
cell.textLabel.text=[dict valueForKey:#"companyTwitter"];
if(indexPath.row == 3)
cell.textLabel.text=[dict valueForKey:#"companyWebsite"];
}
return cell;
}
//Number of sections (first,second and other => 3)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSString *)tableView:(UITableView *)theTableView titleForHeaderInSection:(NSInteger)section
{
NSString * sectionTitle =#"";
switch (section) {
case 0:
sectionTitle = #"title first section";
break;
case 1:
sectionTitle = #"title second section";
break;
case 2:
sectionTitle = #"title other section";
break;
default:
break;
}
return sectionTitle;
}
//Count number of record for sections
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int nRecords=0;
int idSection =0;
//Count number of items for specified section
for (NSDictionary *dict in records){
idSection = [[dict valueForKey:#"idsection"] intValue];
if(section == idSection){
nRecords = [[dict allKeys] count] -1 ; //All dictionary Keys - 1 (the first key "idsection")
}
}
return nRecords;
}
#end

tableview will not update on phone

If this is a repost, I apologize, but I have been scouring the net and cant seem to find anything that works. I have a list of workouts that I display in a tableview that are gathered in plists in the bundle. There is a also a separate tab that I have that allows a user to build their own workouts and save them in the documents folder plist file. Once they are saved, they are added to the table view. In the simulator, everyuhting works fine. But on the actual device, it is not updated unless I close the program for an extended period of time, reload the program from xcode, or turn the phone off. I have tried adding [[self tableview] reload] to "viewDidLoad", "viewWillappear", and "viewDidAppear" and none of them work. Once again, the file is saved, the updating does work in the simulator, and it doesn't update in the phone right away. Any suggestions? Thanks.
Edit: i know it is a long piece of code, but should be straight forward (hopefully lol)
#import "BIDWODList.h"
#import "BIDWODDetails.h"
#define kFileName #"SavedDFWorkouts.plist"
#interface BIDWODList ()
#end
#implementation BIDWODList
#synthesize names;
#synthesize savedNames;
#synthesize keys;
#synthesize details;
#synthesize wodType;
#synthesize benchmarkGirls;
#synthesize theNewGirls;
#synthesize heroes;
#synthesize savedDFGWorkouts;
#synthesize chosenWOD;
#synthesize chosenDetails;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *buildBenchmarkGirls = [[NSMutableArray alloc] init];
NSMutableArray *buildTheNewGirls = [[NSMutableArray alloc] init];
NSMutableArray *buildHeroes = [[NSMutableArray alloc] init];
NSBundle *bundle = [NSBundle mainBundle];
NSURL *plistURL = [bundle URLForResource:#"CrossfitWOD" withExtension:#"plist"];
//put the contents of the plist into a NSDictionary, and then into names instance variable
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:plistURL];
self.names = dictionary;
//take all the keys in the dictionary and make an array out of those key names
self.keys = [self.names allKeys];
for (NSString *nameCheck in keys){
self.details = [names valueForKey:nameCheck];
if ([[self.details valueForKey:#"Type"] isEqualToString:#"The Benchmark Girls"]) {
[buildBenchmarkGirls addObject:nameCheck];
}else if ([[self.details valueForKey:#"Type"] isEqualToString:#"The New Girls"]) {
[buildTheNewGirls addObject:nameCheck];
}else {
[buildHeroes addObject:nameCheck];
}
}
NSString *filePath = [self dataFilePath];
NSMutableDictionary *savedWorkout = [[NSMutableDictionary alloc]initWithContentsOfFile:filePath];
self.savedNames = savedWorkout;
self.savedDFGWorkouts = [[savedWorkout allKeys] sortedArrayUsingSelector:#selector(compare:)];
self.benchmarkGirls = [buildBenchmarkGirls sortedArrayUsingSelector:#selector(compare:)];
self.theNewGirls = [buildTheNewGirls sortedArrayUsingSelector:#selector(compare:)];
self.heroes = [buildHeroes sortedArrayUsingSelector:#selector(compare:)];
//[[self tableView] reloadData]; //reloads the data in case a DFG workout was saved
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.names = nil;
self.keys = nil;
self.benchmarkGirls = nil;
self.theNewGirls = nil;;
self.heroes = nil;
self.details = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSString *)dataFilePath {
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFileName];
}
-(void)viewDidAppear:(BOOL)animated{
[[self tableView] reloadData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 4;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return [benchmarkGirls count];
}else if (section == 1){
return [theNewGirls count];
}else if (section == 2){
return [heroes count];
}else{
return [savedDFGWorkouts count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
static NSString *SectionsTableIdentifier = #"SectionsTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionsTableIdentifier];
}
if (section == 0) {
cell.textLabel.text = [benchmarkGirls objectAtIndex:row];
}else if (section == 1) {
cell.textLabel.text = [theNewGirls objectAtIndex:row];
}else if (section == 2) {
cell.textLabel.text = [heroes objectAtIndex:row];
}else{
cell.textLabel.text = [savedDFGWorkouts objectAtIndex:row];
}
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section == 0) {
return #" The Benchmark Girls";
}else if (section == 1){
return #"The New Girls";
}else if (section ==2){
return #"The Heroes";
}else{
return #"Saved DFG Workouts";
}
}
#pragma mark - Table view delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
BIDWODDetails *destination = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
if (section == 0) {
self.chosenWOD = [self.benchmarkGirls objectAtIndex:row];
self.chosenDetails = [names objectForKey:chosenWOD];
}else if (section == 1) {
self.chosenWOD = [self.theNewGirls objectAtIndex:row];
self.chosenDetails = [names objectForKey:chosenWOD];
}else if (section ==2) {
self.chosenWOD = [self.heroes objectAtIndex:row];
self.chosenDetails = [names objectForKey:chosenWOD];
}else {
self.chosenWOD = [self.savedDFGWorkouts objectAtIndex:row];
self.chosenDetails = [savedNames objectForKey:chosenWOD];
}//end if
//self.chosenDetails = [names objectForKey:chosenWOD];
//[destination setValue:chosenWOD forKey:#"chosenWOD"];
//[destination setValue:chosenDetails forKey:#"chosenDetails"];
destination.chosenWOD = self.chosenWOD;
destination.chosenDetails = self.chosenDetails;
}
#end
Different behaviour between the simulator and the device is often related to incorrect case being used in filenames - the simulator isn't case sensitive, and the device is. Check that you have the correct case used everywhere you reference the plist file.
Alternatively, on the simulator you are able to write directly to the application bundle, but on the device this is not possible and you can only write to certain directories in your application's sandbox, typically the documents directory. You would normally copy a plist to the documents directory on first run, and use that file thereafter.
If I understand right your code you load plist file only in viewDidLoad, but most likely this function called only when you first time load your view. To make it work you should load plist
in viewDidAppear. Something like this:
- (void)viewDidAppear {
NSBundle *bundle = [NSBundle mainBundle];
NSURL *plistURL = [bundle URLForResource:#"CrossfitWOD" withExtension:#"plist"];
//put the contents of the plist into a NSDictionary, and then into names instance variable
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:plistURL];
self.names = dictionary;
//take all the keys in the dictionary and make an array out of those key names
self.keys = [self.names allKeys];
for (NSString *nameCheck in keys){
self.details = [names valueForKey:nameCheck];
if ([[self.details valueForKey:#"Type"] isEqualToString:#"The Benchmark Girls"]) {
[buildBenchmarkGirls addObject:nameCheck];
}else if ([[self.details valueForKey:#"Type"] isEqualToString:#"The New Girls"]) {
[buildTheNewGirls addObject:nameCheck];
}else {
[buildHeroes addObject:nameCheck];
}
}
NSString *filePath = [self dataFilePath];
NSMutableDictionary *savedWorkout = [[NSMutableDictionary alloc]initWithContentsOfFile:filePath];
self.savedNames = savedWorkout;
self.savedDFGWorkouts = [[savedWorkout allKeys] sortedArrayUsingSelector:#selector(compare:)];
[self.tableView reloadData];
}
If it works in the Simulator and does not on the phone, almost for sure the problem is a timing issue. Saving files on a real phone takes much longer than on the simulator.
You should do the following:
when you save a file, log it, and log the return code from the save. If the way you save does not provide a return code, use NSFileManager to verify the file is in fact where it should be and even the size of it. This takes time to do but you should do it.
when your table is asking for the number of this and that, log it, and lot what is returned. You may find that that this comes before the files are saved.
It takes time and effort, but if you start logging all relevant things, you can find it. I just spend 6 hours today tracking down a race condition I had thought could never happen, and it was only after looking at a huge trail of messages that I was able to see the problem.
Almost for sure you will see that either file is not saved, its not where you thought it was, or that the phone timing means some events happen later than they do in the Simulator.

How can I index a UITableView with the user's iPod Library artists?

I want to fetch and index all the iPod Library artists in my app, just like the Music app does. The problem I'm having is that I don't know what's the best way to tackle this problem. Any help?
Let's name your UITableViewController class LibraryArtistsBrowserTableViewController
1. Preparation
The interface file LibraryArtistsBrowserTableViewController.h will contain the following variables:
#interface LibraryArtistsBrowserTableViewController : UITableViewController
#property (nonatomic, strong) MPMediaQuery *artistsQuery;
#property (nonatomic, strong) NSArray *artistsArray,*sectionedArtistsArray;
#property (nonatomic, strong) UILocalizedIndexedCollation *collation;
- (NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector;
#end
2. Indexing
In your implementation file LibraryArtistsBrowserTableViewController.m, put the following code in your viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
//Make a query for artists
self.artistsQuery = [MPMediaQuery artistsQuery];
//Group by Album Artist
[self.artistsQuery setGroupingType:MPMediaGroupingAlbumArtist];
//Grab the "MPMediaItemCollection"s and store it in "artistsArray"
self.artistsArray = [self.artistsQuery collections];
//We then populate an array "artists" with the individual "MPMediaItem"s that self.artistsArray` contains
NSMutableArray *artists = [NSMutableArray array];
for (MPMediaItemCollection *artist in artistsArray) {
//Grab the individual MPMediaItem representing the collection
MPMediaItem *representativeItem = [artist representativeItem];
//Store it in the "artists" array
[artists addObject:representativeItem];
}
//We then index the "artists" array and store individual sections (which will be the alphabet letter and a numbers section), all containing the corresponding MPMediaItems
self.sectionedArtistsArray = [self partitionObjects:artists collationStringSelector:#selector(albumArtist)];
}
The function we use to section the items is defined below, place this somewhere your implementation file:
- (NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count];
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
for(int i = 0; i < sectionCount; i++)
[unsortedSections addObject:[NSMutableArray array]];
for (id object in array)
{
NSInteger index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (NSMutableArray *section in unsortedSections)
[sections addObject:[self.collation sortedArrayFromArray:section collationStringSelector:selector]];
return sections;
}
We then make sure the view controller knows how many sections and rows in each section there are, along with the titles of each section (the indexed letters/numbers):
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[self.collation sectionTitles] objectAtIndex:section];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [self.collation sectionIndexTitles];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.collation sectionTitles] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.sectionedArtistsArray objectAtIndex:section] count];
}
3. Displaying the artists
We then display the artists as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
//We grab the MPMediaItem at the nth indexPath.row corresponding to the current section (or letter/number)
MPMediaItem *temp = [[self.sectionedArtistsArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
//Title the current cell with the Album artist
cell.textLabel.text = [temp valueForProperty:MPMediaItemPropertyAlbumArtist];
return cell;
}
You should be good to go. The only issue I have with this is that it fails to escape punctuation (apostrophes etc.) and 'The' prefix of artists. Other than that it works just fine.
You should read the iPod Library Access Guide. You'll need to make a request and fill your table view with the result. Depending on what you want you could use the MPMediaPickerController. If you just want the artists you should use an MPMediaQuery
MPMediaQuery *artistQuery = [MPMediaQuery artistsQuery];
EDIT:
NSString *artistKey = [MPMediaItem titlePropertyForGroupingType:MPMediaGroupingArtist];
MPMediaQuery *artistsQuery = [MPMediaQuery artistsQuery];
NSMutableArray *artists = [NSMutableArray arrayWithCapacity:artistsQuery.collections.count];
for (MPMediaItemCollection *album in artistsQuery.collections) {
MPMediaItem *albumItem = [album representativeItem];
[artists addObject:[albumItem valueForProperty:artistKey]];
}
The Documentation for reprsentativeItem states:
The media items in a collection typically share common property
values, owing to how the collection was built. For example, if you
build a collection based on a predicate that uses the
MPMediaItemPropertyArtist property, all items in the collection share
the same artist name. You can use the representativeItem property to
efficiently obtain values for such common properties—often more
efficiently than fetching an item from the items array.
Might want to look at UILocalizedIndexedCollation
- (NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count];
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
for (id object in array)
{
NSInteger index = [collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
Place that up top, and use it like so:
self.sectionedArray = [self partitionObjects:ArrayOfItemsFromQuery collationStringSelector:#selector(title)];
sectionedArray being an NSArray declared in .h.
I think this only works for a list of songs though.

How to set Grouped UITableview Section names without a NSFetchedResultsController in play

I have used a number of grouped tables tied to core data managed objects, where the sectionNameKeyPath value is used to identify the attribute in the data that should be used to denote sections for the table.
But how do I indicate the "sectionNameKeyPath equivalent" when I have a table that is being use to present an NSMutableArray full of objects that look like this:
#interface SimGrade : NSObject {
NSNumber * scoreValue;
NSString * commentInfo;
NSString * catName;
}
I would like to have sections defined according to the "catName" member of the class.
Consider, for example that my mutablearray has 5 entries where the 5 "catName" values are "Blue", "Blue", "Blue", "Red", and "Red". So I'd want the number of sections in the table for that example to be 2.
So, what I would 'like to do' could be represented by the following pseudo-code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return (The number of unique catNames);
}
Note: My interest in doing this is not so much for displaying separate sections in the table, but rather so that I can more easily calculate the sums of scoreValues for each category.
<<<<< UPDATE >>>>>>>>>
Joshua's help, as documented in his response has been right on. Here are the two new handlers for number of sections and number of rows per section...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSMutableSet *counter = [[NSMutableSet alloc] init];
[tableOfSimGrades enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[counter addObject:[object catName]];
}];
NSInteger cnt = [counter count];
[counter release];
NSLog(#">>>>> number of sections is -> %d", cnt);
return cnt;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSMutableDictionary *counter = [[NSMutableDictionary alloc] init];
NSMutableArray *cats = [[NSMutableArray alloc] init];
__block NSNumber *countOfElements;
[tableOfSimGrades enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// check the dictionary for they key, if it's not there we get nil
countOfElements = [counter objectForKey:[object catName]];
if (countOfElements) {
// NSNumbers can't do math, so we use ints.
int curcount = [countOfElements intValue];
curcount++;
[counter setObject:[NSNumber numberWithInt:curcount] forKey:[object catName]];
NSLog(#">>>> adding object %d to dict for cat: %#", curcount, [object catName]);
} else {
[counter setObject:[NSNumber numberWithInt:1] forKey:[object catName]];
[cats addObject:[object catName]];
NSLog(#">>>>> adding initial object to dict for cat: %#", [object catName]);
}
}];
countOfElements = [counter objectForKey:[cats objectAtIndex: section]];
int catcount = [countOfElements intValue];
[counter release];
[cats release];
return catcount;
}
My current issue with this routine now lies in the following function... It is ignorant of any sections in the nsmutableArray and so for each section, it starts at index 0 of the array instead of at the 0th element of the appropriate section.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
SimGrade *tmpGrade = [[SimGrade alloc] init];
tmpGrade = [tableOfSimGrades objectAtIndex: indexPath.row];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Category: %#", tmpGrade.catName];
// [tmpGrade release];
return cell;
}
How do I transform the "indexpath" sent to this routine into the appropriate section of the mutableArray?
Thanks,
Phil
You could do something like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSMutableSet *counter = [[NSMutableSet alloc] init];
[arrayOfSims enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
[counter addObject:object.catName];
}];
NSInteger cnt = [counter count];
[counter release];
return cnt;
}
you'd probably want to memoize that, for performance reasons (but only after profiling it).
--- EDIT ---
You can use an NSMutableDictionary, too, to get counts of individual categories.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSMutableDictionary *counter = [[NSMutableDictionary alloc] init];
__block NSNumber *countOfElements;
[arrayOfSims enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// check the dictionary for they key, if it's not there we get nil
countOfElements = [counter objectForKey:object.catName];
if (countOfElements) {
// NSNumbers can't do math, so we use ints.
int curcount = [countOfElements intValue];
curcount++;
[counter setObject:[NSNumber numberWithInt:curcount] forKey:object.catName];
} else {
[counter setObject:[NSNumber numberWithInt:1] forKey:object.catName];
}
}];
NSInteger cnt = [counter count];
// we can also get information about each category name, if we choose
[counter release];
return cnt;
}

sort data and display in UItableview on iphone

i am using this query to get the data by ASC order
select * from list_tbl where cat_id=%d order by names ASC
now my table view shows data based on ASC order
then i used this to get the letters from A to Z on right side
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)help
{
return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:help];
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index];
}
now my problem is how to divide data based on section header like all A content goes to A and then B till Z and when user click on any alphabet on right side it should goes to that section
// UPDATES ///////////////
-(void)setUpRecordsForIndexing
{
for (NSString * recordName in appDelegate.catLists)//where app.catList contains all the value
{
NSString *firstLetter = ([recordName length] ==0)?miscKey:[[recordName substringToIndex:1]uppercaseString];
/*if(![firstLetter beginsWithCharacterInSet:[NSCharacterSet letterCharacterSet]] || [recordName isEqualToString:#""] || recordName == nil )
firstLetter = miscKey ;
*/
NSMutableArray *indexArray = [IndexedRecords objectForKey:firstLetter];
if (indexArray == nil)
{
indexArray = [[NSMutableArray alloc] init];
[IndexedRecords setObject:indexArray forKey:firstLetter];
[IndexLetters addObject:firstLetter];
[indexArray release];
}
[indexArray addObject:record];// this is not workking
// NSLog(#" index array is %#",indexArray);
}
}
my INDEXRECORD is not showing correct value
i can now divide the section based on ABCD but each sections are having same content , what i am doing wrong
for example: say RecordsArray contains your all the records, now in table view datasource method numberOfSectionsInTableView call one more method to index your all the records like below
-(void)setUpRecordsForIndexing
{
if(IndexedRecords)
{
[IndexedRecords release];
IndexedRecords = nil;
}
if(IndexLetters)
{
[IndexLetters release];
IndexLetters = nil;
}
IndexedRecords = [[NSMutableDictionary alloc] init];
IndexLetters = [[NSMutableArray alloc] init];
NSString *miscKey = #"#";
for (NSString * recordName in RecordsArray)
{
NSString *firstLetter = ([recordName length] ==0)?miscKey:[[recordName substringToIndex:1]uppercaseString];
if(![firstLetter beginsWithCharacterInSet:[NSCharacterSet letterCharacterSet]] || [recordName isEqualToString:#""] || recordName == nil )
firstLetter = miscKey ;
NSMutableArray *indexArray = [mIndexedRecords objectForKey:firstLetter];
if (indexArray == nil)
{
indexArray = [[NSMutableArray alloc] init];
[IndexedRecords setObject:indexArray forKey:firstLetter];
[IndexLetters addObject:firstLetter];
[indexArray release];
}
[indexArray addObject:record];
}
}
and in numberOfSectionsInTableView return the count of IndexLetters. I hope this will solve your problem.
While indexing your records, keep your records in a dictionary say IndexedRecords. Such that, for key "A" set the array of records begin with letter "A". And while populating the table view
[[IndexedRecords objectForKey:[IndexLetters objectAtIndex:indexPath.section]]objectAtIndex:indexPath.row]
IndexLetters is also an array containing your index letters for the current list.