I would like to show an index bar on my tableview with all my songs sorted by alphabetical order and those foreign language songs in # just like how the iPod music in iOS do it.
However i gotten an error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[LibraryViewController partitionSongObjects:collationStringSel:]: unrecognized selector sent to instance 0x1454be90'
any comments are greatly appreciated thanks.
//viewDidLoad
[[MediaService sharedService]getAllSongsItemInSongs:^(NSArray *data) {
self.songArray = [[NSMutableArray alloc]initWithArray:data];
}];
NSMutableArray *songNameArray = [NSMutableArray array];
for (Song *songNameItem in self.songArray)
{
[songNameArray addObject:songNameItem.songName];
}
self.sectionArray = [self partitionSongObjects:songNameArray collationStringSel:#selector(songName)];
[self.songLibraryTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
- (NSMutableArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[self.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;
}
you have a simple mistyping method name you call: [self partitionSongObjects:songNameArray collationStringSel:#selector(songName)] whereas you probably should:
[self partitionSongObjects:songNameArray collationStringSelector:#selector(songName)]
Related
If you have an NSArray object named anArray and an NSIndexSet object named anIndexSet, you can iterate forward through an index set as shown in below.
Excerpt, Apple Documents:
NSArray *anArray = [NSArray array];
NSIndexSet *anIndexSet = [NSIndexSet indexSetWithIndex:3];
NSUInteger index = [anIndexSet firstIndex];
while(index != NSNotFound) {
NSLog(#" %#",[anArray objectAtIndex:index]);
index = [anIndexSet indexGreaterThanIndex:index];
}
Why terminating NSRangeException in the above scenario?
I think this example describes the situation better. Also, not used an empty array as you say. Thanks so much rmaddy!!!
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:#"K",#"G",#"G",#"E",#"R",#"G",#"E",#"G",#"G",#"M", nil];
NSIndexSet *anIndexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [mutableArray count])];
NSUInteger index = [anIndexSet lastIndex];
while (index != NSNotFound) {
if ([[mutableArray objectAtIndex:index] isEqualToString:#"G"]) {
[mutableArray removeObjectAtIndex:index];
}
index = [anIndexSet indexLessThanIndex:index];
}
NSLog(#" %#", mutableArray);
I'm creating a UITableView with a list of the users contacts. I've successfully imported the list of contacts and am storing them within an array as a dictionary containing each contact's forename, last name & ID reference. I'm trying to implement the UITableView's indexed list function.
I'm hitting problems when trying to partition the array into the different sections in the table's index UILocalizedIndexedCollation. Specifically by the selector. I want to be able to choose whether to sort the names by the forename or surname.
- (void)getContacts
{
// Do any additional setup after loading the view, typically from a nib.
_addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error) {
NSLog(#"GRANTED");
});
NSArray *rawContactData = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(_addressBook);
NSMutableArray *newContactList = [[NSMutableArray alloc] init];
for (int i = 0; i < [rawContactData count]; i++) {
ABRecordRef contact = (__bridge ABRecordRef)rawContactData[i];
NSDictionary *contactData = #{ #"name" : (__bridge_transfer NSString *)ABRecordCopyValue(contact, kABPersonFirstNameProperty),
#"surname" : (__bridge_transfer NSString *)ABRecordCopyValue(contact, kABPersonLastNameProperty),
#"recordID" : [NSNumber numberWithInt:ABRecordGetRecordID(contact)]};
[newContactList addObject:contactData];
}
_tableData = [NSMutableArray arrayWithArray:[self partitionObjects:newContactList collationStringSelector:#selector(name)]];
}
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count]; //section count is take from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
//put each object into a section
for (NSMutableDictionary *object in array) {
NSInteger index = [collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
Selector is sent to the Object which you are sorting in array. In your case your object is type Dictionary which does not implement selector "name" or surname etc. You should create a simple class instead of using dictionary. and just declare properties to your liking
#interface Contact : NSObject
#property (strong,nonatomic) NSString *fname;
#property (strong,nonatomic) NSString *lname;
#property (strong,nonatomic) NSNumber *rerordId;
#end
You can then use this object instead of dictionary you are using.
Contact *contact= [[Contact alloc]init];
contact.fname = (__bridge_transfer NSString *)
ABRecordCopyValue(contact, kABPersonFirstNameProperty);
contact.lname = (__bridge_transfer NSString *)
ABRecordCopyValue(contact, kABPersonLastNameProperty);
contact.recrordId = [NSNumber numberWithInt:ABRecordGetRecordID(contact)].stringValue;
[newContactList addObject:contact];
Then you can use any name in or attribute selector. Just like in your example you will write the function call sorting with fname for example like this
_tableData = [NSMutableArray arrayWithArray:
[self partitionObjects:newContactList
collationStringSelector:#selector(fname)]];
I am trying to add new items to my collection view using insertItemsAtIndexPaths. My app crashes at performBatchupdate
- (void) addItems {
NSArray *newProducts = #[#"1",#"2",#"3",#"4"];
[self.collectionView performBatchUpdates:^{
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
for (NSInteger index = self.array.count; index < (self.array.count + newProducts.count); index++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
[self.array addObjectsFromArray:newProducts];
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
}
completion:nil];
}
Following is the crash log:
* Assertion failure in -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:]
This Assertion happens when cell is not registered with the collectionview. I am registering my cell.
This worked for me:
If Collection view is empty reload else insertItems.
- (void)addItems {
NSArray *newProducts = #[#"1",#"2",#"3",#"4"];
NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];
for (NSInteger index = self.array.count; index < (self.array.count + newProducts.count); index++) {
[arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
if (self.array) {
[self.array addObjectsFromArray:newProducts];
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];
}
completion:nil];
}
else {
self.array = [[NSMutableArray alloc] initWithArray:newProducts];
[self.collectionView reloadData];
}
}
i'm wondering on how to sort Chinese character into "#" instead of A-Z.
any comments are greatly appreciated.
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[self.collation sectionTitles] count];//section count is take from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
//put each object into a section
for (id object in array)
{
NSInteger index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[self.collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
This is what i ended up doing
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[self.collation sectionTitles] count];//section count is take from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
if ([self.catString isEqualToString:ARTISTS])
{
//put each object into a section
for (id object in array)
{
if (!object)
{
continue;
}
NSInteger index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
}
else
{
NSInteger index;
for (id object in array)
{
Song *songItem = object;
NSString* charIndex;
if([songItem.songName length]<=2)
{
charIndex = [songItem.songName substringToIndex:1];
}
else if([songItem.songName length]<=3)
{
charIndex = [songItem.songName substringToIndex:2];
}
else if([songItem.songName length]<=4)
{
charIndex = [songItem.songName substringToIndex:3];
}
else if([songItem.songName length]>=5)
{
charIndex = [songItem.songName substringToIndex:4];
}
NSRegularExpression *regex = [[NSRegularExpression alloc]
initWithPattern:#"[a-zA-Z]" options:0 error:NULL];
NSUInteger matches = [regex numberOfMatchesInString:charIndex options:0
range:NSMakeRange(0, [charIndex length])];
if (matches >=2)
{
index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
else
{
index = 26; //add object to last index "#"
[[unsortedSections objectAtIndex:index] addObject:object];
}
songItem = nil;
}
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[self.collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
I would like to show an index bar on my tableview with all my songs sorted by alphabetical order and those foreign language songs and numerical in # just like how the iPod music in iOS.
I read the first character of all my song array object exclude duplicates and append it as the index.
How can i filter foreign / non alphabet and numbers ?
this is how it looks like, ugly.
would to show something like iPod.
-(NSMutableArray *)updateSongSectionIndexWithArray:(NSArray*)songArray andSelector:(SEL)selector
{
NSArray *indexedArray = [self partitionObjects:songArray collationStringSelector:selector];
return [[NSMutableArray alloc]initWithArray:indexedArray];
}
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[self.collation sectionTitles] count];//section count is take from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
if ([self.catString isEqualToString:ARTISTS])
{
//put each object into a section
for (id object in array)
{
if (!object)
{
continue;
}
NSInteger index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
}
else
{
for (id object in array)
{
NSInteger index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[self.collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] count];
}
I did a similar solution for a contacts application. I start by defining the alphabet myself, then you can add to each section the items that fit (case insensitive compare for the letters, Characterset for the numbers). After the list is filled, you can remove (or hide) the sections which have no entry.
I did a similar thing for one of my apps. To split titles I used this code:
NSString* alphabet = #"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSArray* indexes = [alphabet componentsSeparatedByString:#""];
for (NSString* title in self.titles) {
char c = [[title uppercaseString] characterAtIndex:0];
NSString* index = [NSString stringWithFormat:#"%c",c];
if ([alphabet rangeOfString:index].location == NSNotFound) {
//The title does not start with a valid letter
index = #"#";
}
NSMutableArray* sectionObjects = [self.sections objectForKey:index];
if (!sectionObjects) {
sectionObjects = [[NSMutableArray alloc] init];
[self.sections setObject:sectionObjects forKey:index];
}
[sectionObjects addObject:title];
}
Obviously this code is still missing the sort part since it's trivial.
I ended up getting this my index to work by this
-(NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[self.collation sectionTitles] count];//section count is take from sectionTitles and not sectionIndexTitles
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
//create an array to hold the data for each section
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
if ([self.catString isEqualToString:ARTISTS])
{
//put each object into a section
for (id object in array)
{
if (!object)
{
continue;
}
NSInteger index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
}
else
{
NSInteger index;
for (id object in array)
{
Song *songItem = object;
NSString* charIndex;
if([songItem.songName length]<=2)
{
charIndex = [songItem.songName substringToIndex:1];
}
else if([songItem.songName length]<=3)
{
charIndex = [songItem.songName substringToIndex:2];
}
else if([songItem.songName length]<=4)
{
charIndex = [songItem.songName substringToIndex:3];
}
else if([songItem.songName length]>=5)
{
charIndex = [songItem.songName substringToIndex:4];
}
NSRegularExpression *regex = [[NSRegularExpression alloc]
initWithPattern:#"[a-zA-Z]" options:0 error:NULL];
NSUInteger matches = [regex numberOfMatchesInString:charIndex options:0
range:NSMakeRange(0, [charIndex length])];
if (matches >=2)
{
NSLog(#"matches %i",matches);
index = [self.collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
else
{
index = 26;
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSLog(#"songItem %# is in index %i",songItem.songName, index);
}
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
//sort each section
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[self.collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}