Friday, August 19, 2011

Coding IOS - the fast way




Although I'm very tempted to get into JQuery Mobile to do my app, I think I'm going to do it in IOS. Some of the reasons for this are:

1) I've already gone through the learning curve - I'd like to take advantage of it.
2) It's more established - I'm less likely to run into frustrating bugs in the platform.
3) I already have a primitive version running - although I may just start from scratch.
4) It has the potential for a richer UI, simply due to the fact it's native.
5) It would like to enhance and reinforce my knowledge of IOS.
6) There is probably a lot more documentation available on IOS at this point.

So, with that in mind, we're off to the IOS races.

Ok, so I think I left off where I was going to upgrade my existing editor to Xcode 4. I talked about it in the blog:

http://gettingintomobile.blogspot.com/2011/08/ios-here-we-come.html

There, we looked at an overview of Xcode 4's new features. So, now I'm just going to open up a project created in Xcode 3, and see what happens.

It's a huge download - 4.2 gig - so it will be a while.

In the meantime, let's pull up the Xcode 3 version and see what we've got.

So, we have our work cut out for us. Right now, it's a spash screen, then a question screen, and when you click on the answer, it moves to another page which displays the correct answer and some totals. What we want to do is have it return to the same page, and highlight the correct answer in green, and the wrong one in red if it was selected. Oh yeah, the data is fake, but we'll get to that later.

So, the "question" display is currently handled by the "rootController". Let's take a look at the methods. Here's the first:


/* call back method on pop of answer page */

- (void) viewWillAppear:(BOOL)animated{

// NSLog(@"view will appear was called");

// won't be animated on the first call
if (animated) {
appState.currentQuestionNumber++;
[appState saveState];
}

currentQuestion = appState.currentQuestion;

answers = [currentQuestion answerArray];

// triggers reload, see veiwDidLoad method
[tableView reloadData];
}



What we're doing is actually setting up the data, and then reloading the "tableView". That's a synthesized variable, so it may be on the nib somewhere. But the main thing for now is to figure out how to get this page to reload itself.

This next method also does some setup:



// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {

QuizAppDelegate *delegate =
(QuizAppDelegate *)[[UIApplication sharedApplication] delegate];

// get answers
appState = delegate.appState;

currentQuestion = appState.currentQuestion;

answers = [currentQuestion answerArray];

// change this to a lable above the table
self.title = @"Jlpt5QuizApp";

tableView.rowHeight = 200;

[super viewDidLoad];
}



The "QuizAppDelegate" is the delegate for the entire app - the one called by the framework. It pulls in a variable called appState which is used for persistent data.

This next method looks to be a callback, apparently to set the parameters of the header (there's only one in this case, which contains the "question", i.e., the Japanese word being quizzed upon.



(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {

NSString * labelText = currentQuestion.questionTxt;

// set a font size
UIFont *labelFont = [UIFont fontWithName:@"Helvetica" size:30.0];

// get a constraint size
CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);

// calculate a label size - takes parameters including the font, a constraint and a specification for line mode
CGSize labelSize = [labelText sizeWithFont:labelFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

// give it a little extra height
return labelSize.height + 20;
}



The next method apparently does more formatting of the the header. All this is for creating the question, i.e., the Japanese word at the top of the display.



- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

// Create label with section title

// allocate label
headerLabel = [[[UILabel alloc] init] autorelease];

// align text to center
headerLabel.textAlignment = UITextAlignmentCenter;

// give it a size
headerLabel.frame = CGRectMake(20, 6, 300, 30);

// give it a color
headerLabel.backgroundColor = [UIColor clearColor];

// give the text a color
headerLabel.textColor = [UIColor whiteColor];

// give it a font
headerLabel.font = [UIFont fontWithName:@"Helvetica-BoldOblique" size:45.0];

// support line break mode for multiline
headerLabel.lineBreakMode = UILineBreakModeWordWrap;

// 0 means any number of lines - necessary for multiline
headerLabel.numberOfLines = 0;

// assign the word to the label
headerLabel.text = currentQuestion.questionTxt;

// very important!
[headerLabel sizeToFit];

// Create header view and add label as a subview

// It' a UIView with a label
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, SectionHeaderHeight)];

// Set the view background color to gray
view.backgroundColor = [UIColor grayColor];

// I'll need to review the autorelease - It's something about
// the creator can't release it, because someone else is going to use it
[view autorelease];

// Here just adding the label to the view
[view addSubview:headerLabel];

// and returning it
return view;
}



This next method assigns text to the choices (one of a possible four in this multiple choice quiz).



// handles the row at the given index path

- (UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

// get the table view cell. Always use the dequeue method
UITableViewCell *cell =
[ tv dequeueReusableCellWithIdentifier:@"cell"];

if( nil == cell ) {

// this sets up a reusable table cell
cell = [ [[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease];

// support line break mode for multiline
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;

// 0 means any number of lines - necessary for multiline
cell.textLabel.numberOfLines = 0;

// set it up with a consistent font with the height calculation (see below)
cell.textLabel.font = [UIFont fontWithName:@"Helvetica" size:30.0];
}


// set the answer

if (indexPath.row < answers.count ) {
NSString *answer = [answers objectAtIndex:indexPath.row];
cell.textLabel.text = answer;
}

return cell;

}



This next one looks like it's just setting the hight of the row.



// get the height of the row

- (CGFloat)tableView:(UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath {

NSString * cellText = [answers objectAtIndex:indexPath.row];

// set a font size
UIFont *cellFont = [UIFont fontWithName:@"Helvetica" size:30.0];

// get a constraint size - not sure how it works
CGSize constraintSize = CGSizeMake(280.0f, MAXFLOAT);

// calculate a label size - takes parameters including the font, a constraint and a specification for line mode
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

// give it a little extra height
return labelSize.height + 20;

}




This is how it decides there will be 4 rows:



- (NSInteger)tableView:(UITableView *)tv
numberOfRowsInSection:(NSInteger)section
{

NSInteger count = answers.count;
if(self.editing) {
count = count + 1;
}
return count;
}






And this is the method that pushes the control to a different "answer" display, on selection of a row. We'll change this so it comes back to the same page. This is going to require some type of mode to determine whether we are in question or answer mode, similar to the Android app.


#pragma mark UITableViewDelegate

- (void)tableView:(UITableView *)tv
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

QuizAppDelegate *delegate =
(QuizAppDelegate *)[[UIApplication sharedApplication] delegate];

AnswersController *answersController =
[[ AnswersController alloc] initWithIndexPath:indexPath];

[ delegate.navController pushViewController:answersController animated:YES];
[ answersController release];

[tv deselectRowAtIndexPath:indexPath animated:YES];
}



It's a amazing how much you forget in a few short months. But, clearly the key at this point is figuring out the best way to return control to the same page.

I'm pretty happy with my decision to go with IOS. I've already got a functional app I can build off of. I'm not starting from scratch as I would've been with JQuery Mobile. This will get me into the App Store faster, and open up another, hopefully more profitable avenue for my app.




No comments:

Post a Comment