We're actually getting to the point where our Vocabulary Quiz App for the iPhone might be starting to have real functionality. We're reading the data, populating the question and answers, have created a space for the english translation, and as of yesterday we have the capability to highlight the correct/incorrect answers and also advance the questions.
So, the next step in the process will have to do with check the selected answer and highlighting it in green if it's right, or red if it's wrong (and highlighting the correct answer in green in that case). So, let's take a look at the code and where we're at.
Actually, we've got a bit of double code from our experiment yesterday. Never a good idea. Let's replace s bunch of code with a call to a previously extracted method:
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
cntr++;
// counter starts at zero, so if % 2 is odd, this means it's
// in "answer mode", that is they've selected an answer
// and we're showing them the results.
if ((cntr % 2) == 0) {
BOOL animated = YES;
[self advance_question:animated];
}
else { // check if question is correct
Ok, we'll fix up the odd cntr logic with boolean logic later on.
Now, once they've selected an answer, how to we judge if it's correct? Essentially, we will do a string comparison between the string corresponding to the selected row and the "hiragana" field, which should really be named "kana", but whatever.
Ok, so we know which one is selected by this:
From the docs, it looks like this is the method we want:
- (id)objectAtIndex:(NSUInteger)index
Here's an example of how it's used from http://www.cocoadev.com/index.pl?NSMutableArray
arrayVariable = [array objectAtIndex:378]; //returns the 379th Object of the array a
And this prints it out:
NSUInteger selection = [indexPath row];
NSString *kana = [answers objectAtIndex:selection];
NSLog(@">>>>>>>>> kana is: %@", kana);
Another notch in the iOS knowledge belt.
Ok, now I'm assuming we have access to the current Question index...
here it is in the header:
Question *currentQuestion;
Let's see if we can print it:
NSLog (@"currentQuestion hiragana: %@", [currentQuestion hiragana]);
Beautiful - check this out:
2011-09-11 14:53:05.243 JlptQuizApp[10875:207] >>>>>>>>> kana is: れっしゃ
2011-09-11 14:53:05.244 JlptQuizApp[10875:207] currentQuestion hiragana: れっしゃ
Ok, now how to check for equal. I think I just did this, but it's always better to work from an example than memory...
It seems complicated:
BOOL result = [string1 caseInsensitiveCompare:string2] == NSOrderedSame;
Ok, this works:
BOOL result = [kana caseInsensitiveCompare:[currentQuestion hiragana]] == NSOrderedSame;
if(result) {
NSLog (@"correct");
}
else {
NSLog (@"incorrect");
}
But I just noticed I'm displaying the kana in the question field, when I should be displaying the kanji.
It's actually getting set in the viewForHeaderInSection:
headerLabel.text = currentQuestion.questionTxt;
So, the questionTxt is unboubtedly getting set in the database reader.
question.questionTxt = kanji;
question.number = cntr_question;
question.hiragana = hiragana;
if ([question.questionTxt length] == 0){
question.questionTxt = hiragana;
}
question.english = english;
Ok, great. I've got the kanji displaying now. I just added the first line in the above. It replaces the kanji with the hiragana/kana if there is no kanji.
Next, I would like to display the english text. Can you change the text of a label from the app, or does it have to be an editable text field? Let's try it.
Ok, if I set it up as an IBOutlet for a UILabel called "english", I can set it as follows:
[english setText:[currentQuestion english]];
And you can see the English at the bottom (ignore the highlights, they're we'll tackle those next:
This is great. After a lot of setup work, things are really moving right along. I'd like to clean up the ugly english label, and also want to add a next button, and of course set up the highlight properly. Let's add the next button first.
Ok, just toss a UIButton on the bottom of the display, create a method to call the advance_question method, set that method up as like this
- (IBAction) nextQuestion;
set up the connection by dragging from the button to file owner in the xib, and, voila - we have our "next Queston" button.
Ok, for now, let's add in the highlighting of the correct and incorrect.
Well, when I initially set up correct answer, I did it with this code:
int rand = arc4random() % 3; // get a number between 0 and 3
appState.slotOfCorrectAnswer = rand;
[answers removeObjectAtIndex: rand];
[answers insertObject:[currentQuestion hiragana] atIndex:rand];
So, actually, I'm maintaining the slot of the correct answer in app state. It might actually make more sense to just loop through the array to find the match, so I don't need to rely on app state. But, if the answers array gets initialized, that kind of assumes appState is around, so let's stick with that for now.
So, if we go to the slot of the current answer, we can see that it's this:
int slotOfCorrectAnswer;
So, what we want to do is compare that int with the row selection. The type of the row selection is given here:
NSUInteger selection = [indexPath row];
So, what exactly is an NSUInteger anyway?
From this site:
http://www.cocoadev.com/index.pl?NSUInteger
We get this recommendation:
Most applications won't need to make the jump to 64-bit yet. However, there's no reason not to be ready to move an application to 64-bit in the future. To ensure that you are ready, you'll want to be sure that you write your code in a manner that's easier to move to a 64-bit later. The best way to do this is to move away from deprecated APIs, such as QuickDraw, and adopt the use of the new NSInteger? and NSUInteger data types that are used throughout the system frameworks in Leopard in place of int and unsigned int. By doing these tasks now, you'll have much less work to do later.
#if __LP64__ || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
This tells us that if the system is 64 bit, it will be long, else it will be an int, of the signed or unsigned type depending. That means this is not an object, and I can probably safely compare the two. But, to be a good boy, I'll go ahead and change the type from in to NSUInteger.
So, now we can try this:
NSUInteger correctRow = [appState slotOfCorrectAnswer];
NSIndexPath *correctAnswerPath = [NSIndexPath indexPathForRow:correctRow inSection:0];
Ok, well, it works to an extent. That is it highlights the the correct row in green *if it wasn't selected*. However, the green highlight doesn't override the blue highlight of the answer selected. Now, we could just stick with blue, but I think green generally has a more understandable connotation. So, what I really need to do is turn off the automatic blue highlighting, so my green or red highlighting can take precedence. Before googling, let's take a quick peek at the docs for UITableView.
Not much help there. Maybe it's because it has to do with the cell. From SO,
http://stackoverflow.com/questions/190908/how-can-i-disable-the-uitableview-selection-highlighting
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
which will still allow the didSelectRowAtIndexPath to be activated. But - where do I call that?
How about about cellForRowAtIndexPath, which is where it gets constructed?
Ahh, perfect:
Things are going swimmingly today. Now, the next step is to just set the incorrect value.
Actually, that is going to be the index path passed into the method, the one selected, via the indexPath parameter.
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
So, we have this logic:
BOOL result = [kana caseInsensitiveCompare:[currentQuestion hiragana]] == NSOrderedSame;
[english setText:[currentQuestion english]];
if(result) {
NSLog (@"correct");
}
else {
NSLog (@"incorrect");
// setting the the selected cell as incorrect
UITableViewCell *incorrectCell = [tv cellForRowAtIndexPath:indexPath];
incorrectCell.backgroundColor = [UIColor redColor];
}
And it works like a dream:
No comments:
Post a Comment