Archive

Archive for September, 2009

iPhone Note #13: Drawing Point, Line, Polygon on top of MKMapview

September 11th, 2009 rupert Comments off

Now I have a reason to study Quartz2D! I am trying to draw a point, line and polygon on top of MKMapview. I will list below important links for reference:

1. Always use Apple’s iPhone OS Reference Library Documentation.. Under “Topics” > “Graphics & Animation” you will find a lot of references, guides and sample code.

2. Start out with the Getting Started with Graphics and Animation. Note, if you ever read the line below and try to find Finger Sketch, please drop me a comment. I could not find it anywhere!

“Next, examine the Finger Sketch sample code in Xcode.”

3. iPhone Application Programming Guide: Graphics and Drawing.

4. Note: I haven’t read everything on Quartz 2D Programming Guide, but I went straight to the Paths Section.

5. Lets look at some sample codes, especially the QuartzDemo.

6. Now, I presume you know IB, add a MKMapView and a UIToolbar at the bottom. Add four UIBarButtonItems for: Map, Point, Line, Polygon. Set MKMapView’s delegate to the File’s Owner. Hook up the UIBarButtonItem IBOutlets to your buttons, so we can change the appearance of the buttons when pressed. Lastly, hook up our IBAction methods for each button.

The Map button allows to pan/zoom on the Map. If any of the three geometry buttons (PT, LN, PG) is pressed, we unhide the GeometryView(which is a UIView) to allow touchEvents to proceed.

TO DO: Watch out for this segment as I will update it with code later.

7. Now to draw a line, we implement the code below in our GeometryView.drawRect method. As we loop through each Pin, we add a line to the point. Afterwards, we stroke the path, thus drawing the lines.

CGContextRef context = UIGraphicsGetCurrentContext();
 
// Drawing lines with a white stroke color
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
 
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
 
if( [pinFactory actualPinCount] > 1){
 
	Pin *pin1 = (Pin *)[[pinFactory pinArray] objectAtIndex:0];
	CGPoint pt1 = pin1.touchLocation;
	CGContextMoveToPoint(context, pt1.x, pt1.y);
 
	for (int i = 1; i < ([pinFactory actualPinCount]); i++)
	{
		Pin *pin2 = (Pin *)[[pinFactory pinArray] objectAtIndex:i];
		CGPoint pt2 = pin2.touchLocation;
		CGContextAddLineToPoint(context, pt2.x, pt2.y);
	}
 
	CGContextStrokePath(context);
}

line.png

8. To draw a polygon, we close the path of lines then fill the polygon.

CGContextClosePath(context);
 
CGContextDrawPath(context, kCGPathFillStroke);

poly.png

Note: I’ll try to post the sample code when I have time. I’m trying to minimize blogging to 10 mins per post as my work tasks/personal development tasks is piling up…

Categories: iphone Tags:

iPhone Note #12: Adding a UIButton on a UITableViewCell (Borderless)

September 9th, 2009 rupert Comments off

1. Im trying to add two UIbuttons to a UITableViewCell, similar to the contacts app. Creating the UIButtons programmatically and adding them to the cell’s subview is trivial. However the interface is a bit different from the contacts app, see image here…

UIButtons-dirty.png

cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ButtonCell"] autorelease];
 
UIButton *buttonLeft = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonLeft setTitle:@"Left" forState:UIControlStateNormal];
[buttonLeft setFrame: CGRectMake( 10.0f, 3.0f, 145.0f, 40.0f)];
[buttonLeft addTarget:self action:@selector(addToFavorites) forControlEvents:UIControlEventTouchUpInside];
 
UIButton *buttonRight = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonRight setTitle:@"Right" forState:UIControlStateNormal];
[buttonRight setFrame: CGRectMake( 165.0f, 3.0f, 145.0f, 40.0f)];
[buttonRight addTarget:self action:@selector(addToFavorites) forControlEvents:UIControlEventTouchUpInside];
 
[cell addSubview:buttonLeft];
[cell addSubview:buttonRight];

2. So to make the background the same as UITableViewStyleGrouped, we can implement:

cell.backgroundColor = [UIColor groupTableViewBackgroundColor];

But I could still see the border of the UITableViewCell.. see image below…
UIButtons-not-so-clean.png

3. The crux of it was to implement a UIView, set the background as UITableViewStyleGrouped and add that as a cell’s backgroundView.

cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ButtonCell"] autorelease];
 
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];
backgroundView.backgroundColor = [UIColor groupTableViewBackgroundColor];
 
cell.backgroundView = backgroundView;
 
UIButton *buttonLeft = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonLeft setTitle:@"Left" forState:UIControlStateNormal];
[buttonLeft setFrame: CGRectMake( 10.0f, 3.0f, 145.0f, 40.0f)];
[buttonLeft addTarget:self action:@selector(addToFavorites) forControlEvents:UIControlEventTouchUpInside];
 
UIButton *buttonRight = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonRight setTitle:@"Right" forState:UIControlStateNormal];
[buttonRight setFrame: CGRectMake( 165.0f, 3.0f, 145.0f, 40.0f)];
[buttonRight addTarget:self action:@selector(addToFavorites) forControlEvents:UIControlEventTouchUpInside];
 
[cell addSubview:buttonLeft];
[cell addSubview:buttonRight];
 
[backgroundView release];

Now the interface is a whole lot cleaner and the resulting image is better…

UIButtons-clean.png

Categories: iphone Tags:

iPhone Note #11: Unit Testing

September 7th, 2009 rupert Comments off

Test first, develop later! That’s the greeting when you visit OCUnit, similar to JUnit. Note that for this tutorial, you do not need to install OCUnit as it comes “built-in” in XCode as of v2.1.

1. Create a new iPhone Window-based application project “SampleTest”.

2. Our subject for testing is Converter.m which converts kilometers to meters. Let’s implement an incorrect conversion by specifying 1km = 100 meters (should be 1000 meters) so we can see that the unit test captures it below…

#import "Converter.h"
 
@implementation Converter
 
- (id)init{
	if(self = [super init]){
 
	}
	return self;
}
 
- (int)convertKilometersToMeters:(int)km{
	return km * 100;
}
 
@end

3. Add another target “UnitTests”. Right click on Targets -> Add -> New Target… -> Choose Unit Test Bundle.

unit-test-bundle.png

4. Name it “UnitTests”. After hitting submit, you will be presented with the project settings for “UnitTests”.

unit-test-name2.png

5. Go to the General Tab -> Click on the “+” icon above “Linked Libraries”. Choose “SampleTest” as the application we have direct dependency with.

unit-test-dependency-3.png

6. Close the Settings. To check, navigate under “Groups & Files” -> Targets. You should see the SampleTest Application Icon just below “UnitTests”.

unit-test-tree-4.png

7. Right Click on “Sample Test” -> Add -> New File…

unit-test-class5.png

8. Name the file “ConverterTest. Don’t forget to also create the header file (default). Specify it also in a different directory under “Location”. Then check the UnitTests as the “Targets”. When you hit “Finish” it will ask you to create the folder “Tests”

unit-test-class6.png

Tip: Keep things organize and put it under a “Tests” Group. Right Click on “Sample Test” -> Add -> New Group… Name it “Tests”, then drag the files (ConverterTest.h and ConverterTest.m) into that group.

9. Open up ConverterTest.h and notice that “SenTestingKit.h” is already imported. Now let’s add method testKilometersToMeters as shown below. Test methods usually start out with a test prefix.

In the implementation, let’s import Converter.h and use STAssertTrue. To test the convertKilometersToMeters method, we are asserting that the result should be 1000. If not, then we should know! That is why we are writing a unit test for.. making sure that our implementation doesn’t break.

#import "ConverterTest.h"
#import "Converter.h"
 
@implementation ConverterTest
 
- (void)testKilometersToMeters{
	int km = 1;
	Converter *converter = [[Converter alloc] init];
	int meters = [converter convertKilometersToMeters:km];
	STAssertTrue(meters == 1000, @"converting %d km to meters should equal 1000, instead received %d", km, meters);
}
 
@end

12. Now, before we build our target “UnitTests”, we need to include additional class references from our application. Drag Converter.m to the “Compile Sources” under UnitTests.

unit-test-drag7.png

13. Now we can build. There are many ways to do this. My preference is to do a clean build when testing. Right Click on Sample Test then choose “Clean SampleTest”. Afterwards choose “Build SampleTest”.

unit-test-build8.png

If you have a succesful build for SampleTest, lets do the same for our “UnitTests”.

unit-test-build9.png

13. Here’s the crux of it. Notice the error in your “Build Results”?

/Users/rupert/projects/iphone/SampleTest/Tests/ConverterTest.m:18: error: -[ConverterTest testKilometersToMeters] : "meters == 1000" should be true. converting 1 km to meters should equal 1000, instead received 100

unit-test-results.png

Now changing the correct implementation of convertKilometersToMeters will put the error away and you will have a successful build.

- (int)convertKilometersToMeters:(int)km{
	return km * 1000;
}

14. Look up the assert methods from SenTest.h.

#import <Foundation/NSObject.h>
#import "SenTest.h"
 
#define STAssertNil(a1, description, ...)
#define STAssertNotNil(a1, description, ...)
#define STAssertTrue(expression, description, ...)
#define STAssertFalse(expression, description, ...)
#define STAssertEqualObjects(a1, a2, description, ...)
#define STAssertEquals(a1, a2, description, ...)
#define STAssertEqualsWithAccuracy(left, right, accuracy, description, ...)
#define STAssertFalseNoThrow(expression, description, ...)
....

References:

http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/UnitTesting/Articles/CreatingTests.html

http://developer.apple.com/tools/unittest.html

Download: SampleTest.zip

Categories: iphone Tags:

iPhone Note #10: Creating Custom Settings

September 4th, 2009 rupert Comments off

settings-overview.gif

1. Create a directory on your desktop, name it “Foo”

2. Download this Sample_plist.txt and place it inside foo. Name it as Root.plist

Foo/
Foo/Root.plist

3. Rename Foo to Settings.bundle

Settings.bundle/
Settings.bundle/Root.plist

4. Drag it to your project.

settings-bundle.gif

5. To retrieve a value from the settings.. For example, if we want to retrieve if the “enabled” switch is on or off?

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL b = [defaults boolForKey:@"enabled_preference"];
Categories: iphone Tags: