Sunday, May 23, 2010

Math Drill: Changing aesthetics and better use of screen space

The previous buttons I had, were functional, however they certainly were not a good use of screen space.  In addition to have them work at all, I had to have a fair bit of gutter space all around my GUI just for the buttons at the left and right sides of the screen.  I did some rewriting so that the buttons will now display directly underneath the choice, and in addition I modified the math problem so that it returns results in numeric order.
The old GUI was this:

The new version displays a set of buttons centered under the current problem.  However if the current problem is at the left or right bounds of the display area, the list of choices will be justified with the display area.
The choice display when centered.  Note the ugly colors.  They were done so that I could check my math and see the bounds of the view.  I'm going to do a color sweep later on to ensure that they are better.







The left edge of the screenand the right edge.

This makes better use of the screen space, and also allows me a little more flexibility.  Note that each math problem currently calculates it's solution space one time, and always displays the same numbers.  That way you can't choose it multiple times and see what numbers remain.


The code



The new math problem result calculations


Not the most elegant code, but N is going to be at most 8, so it's probably more efficient than a more advanced sort to do it 'brute force'.

- (NSArray *)calculateChoices:(int) maxDisplayedValues
{
if (self.resultChoices!=nil)
return self.resultChoices;
NSMutableArray *set=[[NSMutableArray alloc]init];
for (int i=0;i<9;i++)
{
if (i==correctResult)
continue;
[set addObject:[NSNumber numberWithInt:i]];
}
NSNumber *correctResultO=[NSNumber numberWithInt:correctResult];

    NSMutableSet *returnValues=[[NSMutableSet alloc]init];
[returnValues addObject:correctResultO];
for (int i=0;i<(maxDisplayedValues-1);i++)
{
int randomalue=arc4random()%[set count];
NSNumber * value=(NSNumber *)[set objectAtIndex:randomalue];
[set removeObject:value];
[returnValues addObject:value];
if ([set count]==0)
break;
}
[set release];
NSLog(@"Calculated choices count %d,max %d",[returnValues count], maxDisplayedValues);
// N^2, but N is small.
NSMutableArray *res=[[NSMutableArray alloc]init];
while ([returnValues count]>0)
{
NSNumber *min=nil;
for (NSNumber* num in returnValues)
{
if ((min==nil)||([min intValue]>[num intValue]))
min=num;
}
[returnValues removeObject:min];
[res addObject:min];
}
[returnValues release];
self.resultChoices=res;
return res;
}




The new choice display code


Note that in the new display code I calculate the widths of the buttons to include the spacing between them.  I also did a similar modification to have some spacing for the entire problem set.


- (id) initWithChoices:(UIView *) targetView choices:(NSArray*)possibleChoices
{
int maxColumns=4;
int maxRows=2;
int buttonSpacing=5;
int height=targetView.frame.size.height/2;
// Allocate twice the size of the width of the view for buttons.

int selectionViewWidth=targetView.frame.size.width*2;
//There will be maxColumns-1 spaces in the view, so subtract that out before dividing to get the
// width
int targetWidth=(selectionViewWidth-(maxColumns-1)*buttonSpacing)/maxColumns;
// Center the view under the current problem. (in the coordinates of the view that contains the problem.
int originx=targetView.frame.origin.x-selectionViewWidth/2+targetView.frame.size.width/2;
// Put it below the current problem.
int originy=targetView.frame.origin.y+targetView.frame.size.height;
// Clamp it to the leftmost coordinates of the parent view.
if (originx<0)
originx=0;
// Clamp it to the rightmost coordinates of the parent view.
if ((originx+selectionViewWidth)> targetView.superview.frame.size.width)
originx=targetView.superview.frame.size.width-selectionViewWidth;
self.view =[[UIView alloc] initWithFrame:CGRectMake(originx, 
originy, selectionViewWidth, 
targetView.frame.size.height)];
// Ugly background color so I can see the limits of the view.
self.view.backgroundColor=[UIColor colorWithRed:255 
  green:0 blue:255 alpha:129];
int row=0;
int column=0;
for (NSNumber *value in possibleChoices)
{
UIButton *button=[[UIButton alloc]init] ;
button.tag=[value intValue]; 
button.frame=CGRectMake(column*(targetWidth+buttonSpacing),height*row,targetWidth,height-buttonSpacing);
button.backgroundColor=[UIColor redColor];
button.userInteractionEnabled=true;
[button setTitle:[value stringValue] forState:UIControlStateNormal];
[button addTarget:self action:@selector(buttonChosen:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
column++;
if (column>=maxColumns)
{
column=0;
row++;
}
if (row>=maxRows)
break;
}

return self;
}


The new calculations for the math problems


I modified this to allocate enough space at the bottom of the screen for the solutions, it would probably be more 'Apple like' to instead zoom to the problem for extra space, but I like this solution.   I also added a little spacing between the problems themselves.


- (void) displayProblems
{
int numRows=6;
int numColumns=5;
if ((self.interfaceOrientation==UIInterfaceOrientationLandscapeLeft)||
(self.interfaceOrientation==UIInterfaceOrientationLandscapeRight))
{
numRows=5;
numColumns=6;
}
int problemSpacing=10;
// make the bottom margin equal to the a max size of the problems/2.  Note that views outside the
// displayed area don't seem to respond to buton pushes, so we want to stay out of ther if possible.
int bottomMargin=(displayArea.frame.size.height/numRows);
int width=(displayArea.frame.size.width-problemSpacing*(numColumns-1))/numColumns;
int height=(displayArea.frame.size.height-bottomMargin-problemSpacing*(numRows-1))/numRows;
int column=0;
int row=0;
for (MathProblem *problem in currentProblems)
{
problem.view.frame=CGRectMake(column*(width+problemSpacing), (height+problemSpacing)*row, width, height);
column+=1;
if (column>=numColumns)
{
column=0;
row+=1;
}
}
[self setProblemNumber:self.currentProblem];

}

No comments:

Post a Comment