I have a table view in which I'm using sectionIndexTitlesForTableView to display an index. I have to display a list of universities located in different countries. Each Country can have more than one university so the table displays like this:
1. Ecuador
1. Universidad Catolica del Ecuador
2. El Salvador
1. Universidad Don Bosco
3. Estonia
4. Tallin University
4. Tartu Health Care College
4. University of Tartu
4. Finland
5. Aalto University
....
What I intend to do is, using the scroll search at the side, when the user touches "A" it will take you to the first entry with the letter A. For example, when I press "E" it should take me to the section "Ecuador" (displayed on the "image" above).
The problem came at the start because I had multiple sections, when I pressed "B" it would take me to "Australia", the second section (the first being Argentina) instead of taking me to "Belgium" which would be the first Country with the B letter. I managed a workaround of this but it's giving me errors. Here's the code:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
NSArray* bu =[self SectionsASaltar];
int ins =[[bu objectAtIndex:index] intValue];
return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:ins];
}
-(NSArray*)SectionsASaltar
{
NSArray* ALF= [[NSArray alloc] initWithObjects:#"A",#"B",#"C",#"D",#"E",#"F",#"G",#"H",#"I",#"J",#"K",#"L",#"M",#"N",#"O",#"P",#"Q",#"R",#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z", nil];
NSMutableArray* jumps =[[NSMutableArray alloc]initWithObjects:#"0", nil];
int i=0; //contador de elementos
int j=0; //contador del alfabeto
[titl addObject:[self.secc objectAtIndex:i]];
do {
if ([[self.secc objectAtIndex:i] hasPrefix:[ALF objectAtIndex:j]]) {
i++;
}else{
[jumps addObject:[NSString stringWithFormat:#"%d",i]];
i++;
j++;
}
} while (i<[self.secc count]);
[jumps addObject:[NSString stringWithFormat:#"0"]];
NSLog(#"%#",jumps);
return jumps;
}
Where self.secc is my array with the sections and jumps is an array with the number of elements I have to skip on the index to get to next letter.
The idea of the code above is to count how many elements have the letter A, store that and skip them when the users press B, so it will go directly to the first letter B. It works fine for most of the letters but once I press around the letter M it gives me the following error:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (29) beyond bounds (28)'
*** First throw call stack:
(0x25f9012 0x21c6e7e 0x25f8deb 0x25ed7e0 0x13137be 0x1dcd2 0x10d27ee 0x21da705 0x102e920 0x102e8b8 0x10ef671 0x10efbcf 0x124396d 0x12439a6 0x10ee7e2 0x105e16d 0x105e552 0x103c3aa 0x102dcf8 0x32b3df9 0x32b3ad0 0x256ebf5 0x256e962 0x259fbb6 0x259ef44 0x259ee1b 0x32b27e3 0x32b2668 0x102b65c 0x2bbd 0x2af5 0x1)
libc++abi.dylib: terminate called throwing an exception
(lldb)
The thing I really don´t know where I’m going beyond bounds, I think it is in the line :
return [[UILocalizedIndexedCollation currentCollation]
sectionForSectionIndexTitleAtIndex:ins];
but I don´t know how that statement works so I don´t know how to fix it. Any ideas?
Also, is this the best approach for my indexing or is there an easier way to manage same-lettered sections. I know I can make another view, then have the first view have only countries ordered by letters and in a second view the universities, but I’m trying to avoid that.
Your code assumes there is an entry for every letter in the alphabet. If not, then it will increment both i and J, skipping the active entry. So if you have an array that starts with the letters a, c, d, e then your code will test a with a, then c with b, incrementing both indexers and in the next loop it will test the d with c instead of c with c. Because you keep incrementing j you can go past your index of items.
Related
I am trying to compile and run a simple objective c code BUT I am doing it on Windows.I am using the GNU Step and it is extremely hard for me to debug it and understand what is going on runtime.I am a .NET developer and I always use the Debugger in Visual Studio to follow the data flow and stuf but here ...... it is realy annoying.I don't have a Mac Book so I don't have the XCode too.
Can anybody tell me what is the problem in that peace of code?It is quite simple and it would be great if someone who has a Mac could debug it for me and tell me what is wrong.
The idea of the code is that it reads out a text file line by line and then on every 3 lines of code it makes an Object of NSMutableArray and adds it to another NSMutableArray.Here it is:
The read_line function:
int read_line(FILE *in, char *buffer, size_t max)
{
return fgets(buffer, max, in) == buffer;
}
The content of the text file:
Sophie Ellis-Bextor
71222
5.01
Inna Morales
61223
6.00
Kortez Domingues
41231
3.25
The code in the main:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
FILE *in;
if((in = fopen("C:...\\Input.txt", "rt")) != NULL)
{
char line[256];
NSMutableArray* resultArray = [[NSMutableArray alloc] init];
while(read_line(in, line, sizeof line))
{
NSString *currentLine = [[NSString alloc] initWithUTF8String:line];
[resultArray addObject:currentLine];
}
NSMutableArray*resultObjectsArray =[[NSMutableArray alloc] init];
NSMutableArray*tmpArray =[[NSMutableArray alloc] init];
for(int i=0 ; i <[resultArray count];i++)
{
if(i%4 == 3)
{
[resultObjectsArray addObject:tmpArray];
[tmpArray removeAllObjects];
NSLog(#"Here we add a new object");
}
else
{
[tmpArray addObject:[resultArray objectAtIndex:i]];
NSLog(#"%#",[resultArray objectAtIndex:i]);
}
}
fclose(in);
NSLog(#"First object in the result Array: %#",[[resultObjectsArray objectAtIndex:0] objectAtIndex:0]);
}
[pool drain];
All that I can see is that on the
NSLog(#"First object in the result Array: %#",[[resultObjectsArray objectAtIndex:0] objectAtIndex:0]);
line I get the next error:
Uncaught Exception NSRangeException, reason:Index 0 is out of range 0 (in 'objectAtIndex:')
I'm assuming you accidentally left off a (blank) line at the end of your input file, since the exception you mention doesn't happen with the file literally as given. I've edited your question to reflect this. Then, assuming the file is fixed in that way:
The immediate cause of the exception is that tmpArray is empty when the final NSLog is called. The reason for that is that you reuse the same tmpArray object each time through the preceding loop; you add tmpArray to resultObjectsArray, and then clear out tmpArray and start adding additional members to it. The reason this is a problem is that array elements are added by reference, not copied; you need to either copy tmpArray each time or make a brand new temporary object.
So, when you reach the final NSLog, the first element in resultObjectsArray is the same object as tmpArray; you've just called [tmpArray removeAllObjects] in the if(i%4 == 3) conditional, so it's empty; thus the second objectAtIndex:0 raises an exception.
Now, as to why the original version of the input file (the one without the blank line at the end) does not trigger the same exception (but also doesn't function correctly): your for loop goes through line by line, and adds each line to tmpArray, until it reaches a line whose index is evenly divisible by 4, at which point it clears tmpArray so it can start adding more to it the next time. The original version of the input file you gave had 11 lines, so tmpArray wasn't cleared at the end; thus tmpArray, as well as the identical objects serving as elements of resultObjectsArray, contained the last three lines read. Since it wasn't empty, objectAtIndex:0 didn't raise an except. But at the same time, the logic was wrong, since the NSLog is supposed to be returning the first element in the array, but as it happens all the elements are references to that same tmpArray object, which contains only the last stanza of lines.
-(IBAction)someMethod:(UIStepper *)sender{
int x=sender.value; //This is an integer from 0-8;
NSLog(#"%f",sender.value);
NSArray *rpmValues = [[NSArray alloc]initWithObjects:#"a",#"b",#"c",#"d",#"e",#"f",#"g",#"h",#"i", nil];
if (x<=[rpmValues count]) {
myLabel.text = [rpmValues objectAtIndex:x];
}
NSLog(#"%i",[rpmValues count]);
}
Above is my code, what I want to do is to change UILabel display by changing UIStepper. This is very straight forward. But when I change press the stepper value, it crashes:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** - [__NSArrayM objectAtIndex:]: index 1 beyond bounds for empty array'
*** First throw call stack:
And the [rpmValue count] is 9. I really got confused. Can anyone help me?
That code seems fine (see my comment on the question); your problem could arise from the use of
if (x<=[rpmValues count]) {
This will include the count of the array, which exceeds the index range by one. Use
if (x < [rpmValues count]) {
At the very least if (x<=[rpmValues count]) should be if (x<[rpmValues count]). Otherwise if you have an array with, say, two entities then you're allowing yourself to access indices 0, 1 and 2 — three possibilities in total.
Is it possible you've set a maximumValue on your stepper of '9' based on similar logic?
I'm currently working on an app that populates a UITableView with items from a MPMediaItemCollection. I'm trying to add a UITableViewCellAccessoryCheckmark to the row that matches the title of the currently playing track.
I've done so by creating a mutable array of the track titles, which are also set for my cell's textLabel.text property. (for comparison purposes)
Note: This is all done in - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
MPMediaItem *mediaItem = (MPMediaItem *)[collectionMutableCopy objectAtIndex: row];
if (mediaItem) {
cell.textLabel.text = [mediaItem valueForProperty:MPMediaItemPropertyTitle];
}
[mutArray insertObject:cell.textLabel.text atIndex:indexPath.row];
To the best of my knowledge this all works fine except for the below. At this point, I am trying to get the index of the currently playing tracks title and add the UITableViewCellAccessoryCheckmark to that row.
if (indexPath.row == [mutArray indexOfObjectIdenticalTo:[mainViewController.musicPlayer.nowPlayingItem valueForProperty:MPMediaItemPropertyTitle]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
Getting to my question, I added all of the above (mostly irrelevant) code because I'm stumped on where I went wrong. When I log indexOfObjectIdenticalTo: it spits out "2147483647" every time, even though there are never more than 5 objects in the array. But why?
If anyone has any tips or pointers to help me fix this it would be greatly appreciated!
2147483647 just mean the object is not found.
From the documentation of -[NSArray indexOfObjectIdenticalTo:]:
Return Value
The lowest index whose corresponding array value is identical to anObject. If none of the objects in the array is identical to anObject, returns NSNotFound.
and NSNotFound is defined as:
enum {
NSNotFound = NSIntegerMax
};
and 2147483647 = 0x7fffffff is the maximum integer on 32-bit iOS.
Please note that even if two NSString have the same content, they may not be the identical object. Two objects are identical if they share the same location, e.g.
NSString* a = #"foo";
NSString* b = a;
NSString* c = [a copy];
assert([a isEqual:b]); // a and b are equal.
assert([a isEqual:c]); // a and c are equal.
assert(a == b); // a and b are identical.
assert(a != c); // a and c are *not* identical.
I believe you just want equality test instead of identity test, i.e.
if (indexPath.row == [mutArray indexOfObject:[....]]) {
Looking at the docs for NSArray
Return Value
The lowest index whose corresponding array value is identical to anObject. If none of the objects in the array is identical to anObject, returns NSNotFound.
So you should probably do a check
NSInteger index = [array indexOfObjectIdenticalTo:otherObject];
if (NSNotFound == index) {
// ... handle not being in array
} else {
// ... do your normal stuff
}
I have two arrays, each containing strings. The first array is a list of words, the second array contains alternatives to those words in different languages.
The arrays are matched such that the word at index n in the second array is a translation of the word at index n in the first array.
The words and their translations are displayed in a table view. The user can filter the table view by entering text in a search field. When this is done, I create a filtered array from the first array like this:
- (void)filterContentForSearchText:(NSString*)searchText
[self.filteredarray removeAllObjects];
[firstarray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj compare:searchText options:NSCaseInsensitiveSearch range:NSMakeRange(0, [searchText length])] == NSOrderedSame)
{
idx= [firstarray indexOfObjectIdenticalTo:obj];
NSUInteger maxindex = idx + 50;
for (idx ; (idx < [firstarray count] && idx <= maxindex && idx!= NSNotFound); idx ++)
{
[self.filteredarray addObject:[firstarray objectAtIndex: idx]];
}
*stop = YES;
}
}];
Then, when I am displaying the values in my table view, I use the following code. This is an exerpt from my cellForRowAtIndexPath method. I am trying to get the index from the original array using the object that has been added to the filtered array.
contentForThisRow = [self.filteredarray objectAtIndex:row];
NSUInteger index = [self.firstarray indexOfObjectIdenticalTo:contentForThisRow];
contentForThisRow2 = [self.secondarray objectAtIndex:index];
This works on the simulator, but on the device I will sometimes get repeats of the same entry from the second array. For example, my first array contains the word "hello" three consecutive times, at indexes x, y and z. My second array contains "hei", "heisan" and "hoppsan", which are all translations of "hello", at indexes x, y and z.
On the simulator, I get three cells, each with a different translation. On the device, I get three cells, all with "hei", the first translation. This does not happen for all repeated translations.
Why is this happening, and how can I get around it?
I think the problem is that iOS (on the device) may be using a slightly different optimisation to the emulator somewhere, either in NSString or NSArray. That is a guess.
indexOfObjectIdenticalTo: returns the index of the first object that has the same memory address as the object you are passing in. On the phone it appears to have re-used the identical string objects in your first array when building the filtered array (possibly even when building firstArray), so you are getting the same index value back each time.
A better solution would be to build your filtered array as an array of dictionaries, storing the values from the correct indexes of firstArray and secondArray at that point. You can then use these values directly when populating the cell instead of searching through both arrays again. This should also have some performance benefits.
You would achieve this using the following code. First, inside your loop when you are building the filtered array, instead of adding the object from firstarray, do this:
[self.filteredArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:[firstarray objectAtIndex:idx],#"english",[secondarray objectAtIndex:idx],#"translated",nil];
Then, in your cellForRowAtIndexPath, to get your two content variables:
NSDictionary *rowData = [self.filteredarray objectAtIndex:row];
contentForThisRow = [rowData objectForKey:#"english"];
contentForThisRow2 = [rowData objectForKey:#"translated"];
An even better solution would be to hold your data like this in the first place, and not try to keep two separate arrays synchronised. I imagine if you want to add or alter anything in your two separate files you could quickly get them out of step. However, I feel I've done enough for the day...
else
contentForThisRow = [self.firstarray objectAtIndex:row];
contentForThisRow2 = [self.secondarray objectAtIndex:row];
You see anything wrong with that?
So, I'm struggling a bit with my programming project.
I have a object that stores player information, name, wins, losses.
I proceed to use that object in another object (a bracket) that sets the values of the player information, after each loop it copies the player object to a NSMutable in the bracket class.
Within the bracket class I have a method to print out the information, which was easy enough to figure out.
Now, to generate what will happen for the next bracket, I set up a method that will return a bracket when it's finished copying who won or lost.
I've done this all within the same bracket method so I could access the data as easily as possible. How do I simply just make a copy of the player object and add it to the new bracket?
I'm finding that I'm just getting garbage values or nothing at all when I try to print out the Bracket I've just generated. Here's my function that I'm having trouble with.
-(Bracket *) NextRound//Configures the next round
{
int i, y, *selection; //counter and selection
int comp;
y = 1;
i = 0;//so the counter won't get messed up
Bracket *roundx = [Bracket new]; //creates the bracket that will be sent back with the winners.
NSLog(#"Did %#(1) or %#(2) win (1 or 2)?: ",[[playerarray objectAtIndex:i] name], [[playerarray objectAtIndex:y] name]);
scanf("%d",&selection);
comp = selection++;
if (comp == 1)
{
NSLog(#"Player %d %# selected to win", i, [[playerarray objectAtIndex:i] name]);
[[self.playerarray objectAtIndex:i] setNext];//bool value for win or not
[roundx.playerarray addObject: [self.playerarray objectAtIndex:i]];
[roundx Print];//Too see if it has anything, usually it does not.
}
else
{
i++;
NSLog(#"Player %d %# selected to win", i, [[playerarray objectAtIndex:i] name]);
[[self.playerarray objectAtIndex:i] setNext];
[roundx.playerarray addObject: [self.playerarray objectAtIndex:i]];
[roundx Print];
}
return(roundx);
}
So, I thought that would just work. It compiles just fine (I get a few warnings, but it's about the integers and such that I use for logic mostly).
Thank You!
My comments:
The definition of selection is incorrect. You have defined it as a pointer to an int, but you are using it as if it were an int everywhere in your code. Note that selection++ increments selection by sizeof(int) not 1.
You shouldn't really be using -new to initialise objects but -alloc and then -init. (This is the modern convention).
The most likely cause of your problem is that playerarray is not initialised.