Escaping punctuation, and 'The' prefix in song titles when collating a MPMediaQuery - ios

The code works and populates the table with sections, but it has a flaw: It doesn't escape punctuation and 'The' prefixes in song titles, just like how the native music app does.
Would really appreciate some guidance on how I should go about doing this.
- (void)viewDidLoad
{
[super viewDidLoad];
MPMediaQuery *songQuery = [MPMediaQuery songsQuery];
self.songsArray = [songQuery items];
self.sectionedSongsArray = [self partitionObjects:self.songsArray collationStringSelector:#selector(title)];
}
- (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;
}

I completely overlooked this. The answer here is to simply use MPMediaQuerySection. The Apple docs are there for a reason!

Cocotutch -
Here's the implementation that I used to index a query containing all the songs in my music library:
MPMediaQuery *allSongsQuery = [MPMediaQuery songsQuery];
// Fill in the all songs array with all the songs in the user's media library
allSongsArray = [allSongsQuery items];
allSongsArraySections = [allSongsQuery itemSections];
allSongsArraySections is an NSArray of MPMediaQuerySection, each of which has a title and a range. The NSArray object for section zero (with title #"A" in my case) has range.location of 0 and range.length of 158.
I return the range.length value for each section when numberOfRowsInSection is called for my UITableView. I use the range.location value in cellForRowAtIndexPath as the starting row of the section, and then add indexPath.row to it in order to arrive at the cell I need to return from my allSongsArray.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
....
// Return the number of rows in the section.
MPMediaQuerySection *allSongsArraySection = globalMusicPlayerPtr.allSongsArraySections[section];
return allSongsArraySection.range.length;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
MPMediaQuerySection *allSongsArraySection = globalMusicPlayerPtr.allSongsArraySections[indexPath.section];
rowItem = [globalMusicPlayerPtr.allSongsArray objectAtIndex:allSongsArraySection.range.location + indexPath.row];
....
}
Before using this I had tried to match the native music player's implementation by writing my own, and it didn't behave quite identically. Not only that, but the native indexing is MUCH faster!

Related

Adding sections to tableview, index out of bounds error

In this app, I download data from Parse. That data is a list of 5 Messages, each with a category. There are 4 different categories as two of the messages have the same category. I want to put that data into sections on a tableview. Since the data is not ready for sectioning, I had to create 2 mutable arrays that act like index look ups. (followed this guide: https://parse.com/questions/using-pfquerytableviewcontroller-for-uitableview-sections)
Problem: I'm getting this error:
-[__NSArrayM objectAtIndex:]: index 8 beyond bounds [0 .. 4]'
Question is, why am I getting this error and how do I fix it?
I've located the exact line that is the problem. First, here is what you need.
1st Mutable Dictionary:
self.sectionToCategoryMap //This property maps the sections titles to the row indeces for the data. The output it looks like this: (read as, get the 1st category header from object at index 0)
0 = "Section Header 1";
24 = "Section Header 2";
16 = "Section Header 3";
32 = "Section Header 4";
2nd Mutable Dictionary:
self.sections // This maps what items are in what section(category). Output looks like this:
"category 1" =(32);
"category 2" =(24);
"category 3" =(16);
"category 4" =(0,8);
These two Dictionaries are created by this code:
- (void)prepSections:(id)array {
[self.sections removeAllObjects];
[self.sectionToCategoryMap removeAllObjects];
self.sections = [NSMutableDictionary dictionary];
self.sectionToCategoryMap = [NSMutableDictionary dictionary];
NSInteger *section = 0;
NSInteger *rowIndex = 0;
for (MessageItem *messageItem in self.messageList) {
NSString *category = [messageItem valueForKey:#"messageCategory"]; //retrieves category for each message -1st regulator
NSMutableArray *objectsInSection = [self.sections objectForKey:category]; //assigns objectsinsection value of sections for current category
if (!objectsInSection) {
objectsInSection = [NSMutableArray array];
// this is the first time we see this category - increment the section index
//literally it ends up (0:Regulatory)
[self.sectionToCategoryMap setObject:category forKey:[NSNumber numberWithInt:rowIndex]];
section++;
}
[objectsInSection addObject:[NSNumber numberWithInt:(int)rowIndex++]]; //adds message to objects in section
[self.sections setObject:objectsInSection forKey:category]; //adds dict of objects for category
}
}
The error is happening in my in cellForRowAtIndexPath below, specifically the line:
NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row];
(note: categoryForSection is a helper method I defined, its implementation is also below.)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSString *category= [self categoryForSection:indexPath.section];
NSArray *rowIndecesInSection = [self.sections objectForKey:category];
NSNumber *rowIndex = [rowIndecesInSection objectAtIndex:indexPath.row]; //pulling the row indece from array above
//gets to 3 and breaks!!!
messageItem = [self.messageList objectAtIndex:[rowIndex intValue]];
[cell configMessageCell:messageItem indexPath:indexPath];
return cell;
}
For good measure, here is the rest of my code.
- (NSString *) categoryForSection:(NSInteger*)section { //takes section # and returns name of section.
return [self.sectionToCategoryMap objectForKey:[NSNumber numberWithInt:(int)section]];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return (unsigned long)self.sections.allKeys.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *category = [self categoryForSection:section];
NSArray *rowIndecesInSection = [self.sections objectForKey:category];
return [rowIndecesInSection count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *category =[self categoryForSection:section];
return category;
}
Please help me fix this. It has had me stuck for days! Thank you!
Matt
I think the problem lies in declaring these as pointers:
NSInteger *section = 0;
NSInteger *rowIndex = 0;
(Note the weird multiples of 8 in the numbers in your dictionaries - that's because pointer arithmetic works differently from "normal" arithmetic). Try with
NSInteger section = 0;
NSInteger rowIndex = 0;

Adding section and index list with custom cell, searchdisplaycontroller

I have created a UITableView with custom cell & stored name,no,pincode in to these cell.
Here is my Code for array:-
for (int i =0; i<[tempArr count]; i++)
{
NSString *rawData = [tempArr objectAtIndex:i];
if (rawData !=nil)
{
Persons *newPerson = [[Persons alloc]init];
NSArray *data = [rawData componentsSeparatedByString:#"\t"];
newPerson.name = [NSString stringWithFormat:#"%#",[data objectAtIndex:0]];
newPerson.no = [[data objectAtIndex:1] integerValue];
newPerson.pincode = [[data objectAtIndex:2] integerValue];
[allPersons addObject:newPerson];
}
}
Here is my Customcell.h
#interface Customcell : UITableViewCell
#property(weak) Persons* person;
#end
UITableView Datasrouce method:-
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Customcell *cell = [tblStations dequeueReusableCellWithIdentifier:#"personCell"];
if (tableView == self.searchDisplayController.searchResultsTableView)
{
cell.person = filteredContentList[indexPath.row];
[cell.textLabel setText:cell.person.name];
}
else
{
cell.person = allPersons[indexPath.row];
[cell.textLabel setText:cell.person.name];
}
return cell;
}
How do i create Section & index list for all names from A to Z & give title by cell.textLabel.text?
I am following This Tutorial but it has static keys & names added to NSDictionary,NSArray.
In my example i do not know how many names starting with same letter can come in the array. i am also using UISearchDisplayController for search person name.
I want to add number of sections & title for those sections by names that is in the array or cell.textLabel.text dynamically.
i do not know about UISearchDisplayController that these sections & index list will be displaying in UISearchDisplayController so i do not want these sections & index list while searching.
You need to spend a little more time trying to make your questions more clear.
Include a custom implementation of the necessary UITableView data source and delegate methods...
NOTE my assumption that your variable allPersons is an NSMutableArray.
NOTE these do not include for your search results data sets!
Return an NSInteger for the number of sections in your UITableView...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSSet *setData = nil;
NSInteger integerData = 0;
setData = [NSSet setWithArray:allPersons];
integerData = [setData count];
return integerData;
}
UPDATE
Return an NSString for section header titles...
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSOrderedSet *setData = nil;
NSString *stringData = nil;
setData = [NSOrderedSet orderedSetWithArray:allPersons];
stringData = [[setData allObjects] componentsJoinedByString:#" "];
return stringData;
}
...plus others if I have the time...

tableView Index # for Numbers?

I have an indexed tableView that displays a list of songs from the user's iPod library. But the sections start with 1, 2, etc., because I have songs that start with numbers. How do I group all songs that start with a number into a section '#' in my index?
Right now this is my app (left) compared to what I'd like it to be (right, Music.app). I've highlighted where the # should go (at the end of my index):
This is my code so far in my viewDidLoad:
songsQuery = [MPMediaQuery songsQuery];
songs = [songsQuery items];
NSMutableDictionary *songsInitials;
songsInitials = [[NSMutableDictionary alloc]init];
self.alphabetArray = [[NSMutableArray alloc] init];
for (int i=0; i< songs.count; i++)
{
NSString *firstletter=[[[songs objectAtIndex:i] valueForProperty:MPMediaItemPropertyTitle] substringToIndex:1];
if (songsInitials[firstletter] == nil) {
songsInitials[firstletter] = [[NSMutableArray alloc] init];
[self.alphabetArray addObject:firstletter];
}
[songsInitials[firstletter] addObject:songs[i]];
}
[self.alphabetArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)]; //sorting array in ascending array
self.songsInitials = songsInitials;
....and my tableView data source:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _alphabetArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_songsInitials[_alphabetArray[section]] count];
}
-(NSString *)titleForRow:(NSIndexPath *)indexpath{
NSString *firstLetter = _alphabetArray[indexpath.section];
MPMediaItem *myItem = [_songsInitials[firstLetter] objectAtIndex:indexpath.row];
return [myItem valueForProperty:MPMediaItemPropertyTitle];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
return self.alphabetArray;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index{
NSIndexPath *indexpath;
for (int i=0; i < self.alphabetArray.count; i++)
{
NSString *titleToSearch=[self.alphabetArray objectAtIndex:i]; //getting sectiontitle from array
if ([title isEqualToString:titleToSearch]) // checking if title from tableview and sectiontitle are same
{
indexpath=[NSIndexPath indexPathForRow:0 inSection:i];
// scrolling the tableview to required section
[self.songsTable scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionTop animated:YES];
break;
}
}
return indexpath.section;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.text= [self titleForRow:indexPath]; //getting cell content
return cell;
}
This is what I've tried so far, adjusting the for loop in viewDidLoad:
for (int i=0; i< songs.count; i++)
{
NSString *firstletter=[[[songs objectAtIndex:i] valueForProperty:MPMediaItemPropertyTitle] substringToIndex:1];
if (songsInitials[firstletter] == nil) {
NSString *songsInitialsFirstLetter = songsInitials[firstletter];
if ([self stringIsNumeric:songsInitialsFirstLetter]){
songsInitials[firstletter] = [[NSMutableArray alloc] init];
[self.alphabetArray addObject:[NSString stringWithFormat:#"#"]];
NSLog(#"There are numbers!");
}
else if (![self stringIsNumeric:songsInitialsFirstLetter]){
songsInitials[firstletter] = [[NSMutableArray alloc] init];
[self.alphabetArray addObject:firstletter];
}
}
[songsInitials[firstletter] addObject:songs[i]];
}
-(BOOL) stringIsNumeric:(NSString *) str {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *number = [formatter numberFromString:str];
return !!number; // If the string is not numeric, number will be nil
}
...but it didn't work. I've searched around but I can't find any other questions that ask this, am I missing something obvious here? Any help would be much appreciated! :)
Did not test but changing first letter before looking in the arrays would give better result.
After the sort, not sure where the symbol (#) will end tough, you may have to manual push it to the end.
for (int i=0; i< songs.count; i++)
{
NSString *firstletter=[[[songs objectAtIndex:i] valueForProperty:MPMediaItemPropertyTitle] substringToIndex:1];
NSScanner *ns = [NSScanner scannerWithString:firstletter];
if ( [ns scanInt:NULL] )
{
firstLetter = #"#";
}
if (songsInitials[firstletter] == nil) {
songsInitials[firstletter] = [[NSMutableArray alloc] init];
[self.alphabetArray addObject:firstletter];
}
[songsInitials[firstletter] addObject:songs[i]];
}

separate JSON to uitableview sections

I have some JSON data that I am getting from my database. I can pull it fine and load it into my table view. my issue is separating my JSON data so I can section the tableview.
JSON
[{"id":"1","name":"steve mans","phoneNumber":"(559)123-4455","showNumber":"1","greenCard":"1","expiration":"2014-02-15","driver":"1","paid":"1","watch":"1"},
{"id":"2","name":"myself and me","phoneNumber":"(559)321-6784","showNumber":"1","greenCard":"1","expiration":"2013-10-18","driver":"0","paid":"0","watch":"2"},
{"id":"4","name":"tod bellesmithson","phoneNumber":"(559)678-3421","showNumber":"0","greenCard":"1","expiration":"2013-11-22","driver":"1","paid":"0","watch":"2"},
{"id":"3","name":"John Smith","phoneNumber":"(559)123-1234","showNumber":"1","greenCard":"0","expiration":"2013-10-08","driver":"0","paid":"1","watch":"3"},
{"id":"5","name":"greg smith","phoneNumber":"559 345-1234","showNumber":"1","greenCard":"1","expiration":"2013-10-08","driver":"0","paid":"1","watch":"3"}]
What I am trying to do is, separate this data into three sections in my tableview. So I thought create three different tables and load each table into a different section of the tableview. But the information is the same in each one (id, name, phone etc.) So I ended up with one table and added a column that designates what shift people work, 'watch'. So how do I separate the data by using the watch column, so in my tableview i will have:
section one
people who work night shift
section two
people who work morning
section three
night shift
Try with this code:
NSArray *data = (NSArray)[NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&error];
NSMutableArray *morningShift = [NSMutableArray array];
NSMutableArray *noonShift = [NSMutableArray array];
NSMutableArray *nightShift = [NSMutableArray array];
for (int i=0; i< [data count]; i++)
{
NSDictionary *item = data[i];
if (#"1" == item[#"watch"] )
{
[morningShift addObject:item];
} else if (#"2" == item[#"watch"] )
{
[noonShift addObject:item];
} else if (#"3" == item[#"watch"] )
{
[nightShift addObject:item];
}
}
try this
NSMutableArray *tableData = [NSMutableArray alloc] init];
NSArray* nameArr = [NSArray arrayWithObjects: #"1", #"2",#"3",nil];
for(int i=0; i<[nameArr count]; i++)
{
[tableData addObject:[jsonArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"watch = %#",[nameArr objectAtIndex:i]]] ];
}
TableView Delegate Methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [tableData count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[tableData objectAtIndex:section ] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell= nil;
//implement your logic to display the cell item;
NSArray sectionData = [tableData objectAtIndex:indexPath.section];
NSArray rowData = [sectionData objectAtIndex:indexPath.row];
return cell;
}
Please note i have not compiled the code. There is a chance of compilation error.
check for section in your cellForRowAtIndexPath method and load data accordingly,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//initialize cell
if (indexPath.section == 0){
// load data
}
return cell;
}

How to add a search Icon as section for SearchBar in UITableView using search controller

I am trying to add the image for search (magnifying glass) as the section, to easily go to the search bar.
This is the issue I am experiencing:
My code to setup the sections is as follows:
Note: self.fbFriends is an Array of Dictionaries that include the facebook friends of the user.
self.fbFriends = [[NSArray alloc] initWithArray:[[MESCache sharedCache] facebookFriends]];
self.searchResults = [[NSMutableArray alloc] init];
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
[self.sections setValue:[[NSMutableArray alloc] init] forKey:UITableViewIndexSearch];
// Loop through the friends and create our keys
for (NSDictionary *friend in self.fbFriends)
{
NSString *c = [[friend objectForKey:#"name"] substringToIndex:1];
found = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc] init] forKey:c];
}
}
// Loop again and sort the friends into their respective keys
for (NSDictionary *friend in self.fbFriends)
{
[[self.sections objectForKey:[[friend objectForKey:#"name"] substringToIndex:1]] addObject:friend];
}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{
[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]]];
}
Here is my section setup and header views:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return 1;
} else {
return [[self.sections allKeys] count];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return NSLocalizedString(#"FbFriendsSearchControllerSection", #"Fb Friends search controller - section title for search results table view");
} else {
return [[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section];
}
}
Does anyone see how this is incorrect?
This is not a standard practice. There is no need for the extra section just to show a search.
Don't add the "search" section to your self.sections dictionary. Instead, you just have sections for the actual data. Then in your tableView:sectionForSectionIndexTitle:atIndex: method you can do:
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSInteger res = 0;
if (tableView == self.tableView) {
if (index == 0) {
// If the user taps the search icon, scroll the table view to the top
res = -1;
[self.tableView setContentOffset:CGPointMake(0, 0) animated:NO];
} else {
res = ... // return the proper index for your data
}
} else {
res = 0;
}
return res;
}
Side note - you really want to have an array of dictionaries. The main array should represent the sections. As you have it now, you are constantly getting your main dictionary's keys and sorting them. This will be done MANY times - all needlessly. Using an array lets you quickly get the data from the section index.

Resources