Andy Ibanez
Andy Ibanez

Reputation: 12254

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?

Upvotes: 2

Views: 4191

Answers (3)

sooper
sooper

Reputation: 6039

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.

Upvotes: 15

topLayoutGuide
topLayoutGuide

Reputation: 1397

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.

Upvotes: 0

jbat100
jbat100

Reputation: 16827

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.

Upvotes: 0

Related Questions