Archive

Archive for the ‘iphone’ Category

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

August 9th, 2010 rupert 7 comments

UPDATE: Aug 9, 2010
DrawMap2.zip
Note: This does not contain the new MapKit functions for overlaying lines and polygons. This zip was created to compile against 4.0.0 but still have the same codebase.
============================

This is an update to iPhone DevNote #13. This post has solved my zooming/panning problem with a CustomView on top of my MKMapView courtesy of http://spitzkoff.com/craig/?p=108 (Craig’s blog).

The trick here is instead of doing the drawing on the drawRect method of the CustomView, we will use Craig’s methodology to use the drawRect method of a custom MKAnnotationView. Note, that he also used an internal view and made clipsToBounds = NO, this way we can draw the whole geometry on top of MKMapView not just a portion of it. The end result is the shape (polygon in this example) is below the added pins.

polygon_on_top_mapview.png

@interface LinePolygonAnnotationInternalView : UIView
{
	// line view which added this as a subview. 
	LinePolygonAnnotationView* _mainView;
}
@property (nonatomic, retain) LinePolygonAnnotationView* mainView;
@end
 
@implementation LinePolygonAnnotationInternalView
 
@synthesize mainView = _mainView;
 
-(id) init
{
	self = [super init];
	self.backgroundColor = [UIColor clearColor];
	self.clipsToBounds = NO;
 
	return self;
}
 
 
-(void) drawRect:(CGRect) rect
{
	GeometryAnnotation* myAnnotation = (GeometryAnnotation*)self.mainView.annotation;
 
	// only draw our lines if we're not int he moddie of a transition and we 
	// acutally have some points to draw. 
	if(!self.hidden && nil != myAnnotation.points && myAnnotation.points.count > 0)
	{
		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(myAnnotation.geometryType == kGeometryTypePolygon){
			CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
		}
 
		// Draw them with a 2.0 stroke width so they are a bit more visible.
		CGContextSetLineWidth(context, 2.0);
 
		for(int idx = 0; idx < myAnnotation.points.count; idx++)
		{
			CLLocation* location = [myAnnotation.points objectAtIndex:idx];
			CGPoint point = [self.mainView.mapView convertCoordinate:location.coordinate toPointToView:self];
 
			NSLog(@"Point: %lf, %lf", point.x, point.y);
 
			if(idx == 0)
			{
				// move to the first point
				CGContextMoveToPoint(context, point.x, point.y);
			}
			else
			{
				CGContextAddLineToPoint(context, point.x, point.y);
			}
		}
 
		if(myAnnotation.geometryType == kGeometryTypeLine){
			CGContextStrokePath(context);
		}
		else if(myAnnotation.geometryType == kGeometryTypePolygon){
			CGContextClosePath(context);
 
			CGContextDrawPath(context, kCGPathFillStroke);
		}
 
	}
 
 
}
 
-(void) dealloc
{
	self.mainView = nil;
 
	[super dealloc];
}
@end
 
@implementation LinePolygonAnnotationView
 
@synthesize mapView = _mapView;
 
- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
 
		self.backgroundColor = [UIColor clearColor];
 
		// do not clip the bounds. We need the LinePolygonAnnotationInternalView to be able to render the whole line/polygon, regardless of where the
		// actual annotation view is displayed. 
		self.clipsToBounds = NO;
 
		// create the internal line view that does the rendering of the line. 
		_internalView = [[LinePolygonAnnotationInternalView alloc] init];
		_internalView.mainView = self;
 
		[self addSubview:_internalView];
    }
    return self;
}
 
-(void) setMapView:(MKMapView*) mapView
{
	[_mapView release];
	_mapView = [mapView retain];
 
	[self regionChanged];
}
 
-(void) regionChanged
{
	NSLog(@"Region Changed");
 
	// move the internal line view. 
	CGPoint origin = CGPointMake(0, 0);
	origin = [_mapView convertPoint:origin toView:self];
 
	_internalView.frame = CGRectMake(origin.x, origin.y, _mapView.frame.size.width, _mapView.frame.size.height);
	[_internalView setNeedsDisplay];
 
}
 
- (void)dealloc 
{
	[_mapView release];
	[_internalView release];
 
    [super dealloc];
}
 
 
@end

I extended the class above to be able to draw both lines and polygons by checking a property (geometryType) of the GeometryAnnotation. If the geometryType is a line, then just stroke the path. However, if the geometryType is a polygon, then close the path and fill it.

if(myAnnotation.geometryType == kGeometryTypeLine){
	CGContextStrokePath(context);
}
else if(myAnnotation.geometryType == kGeometryTypePolygon){
	CGContextClosePath(context);
 
	CGContextDrawPath(context, kCGPathFillStroke);
}

And here is the GeometryAnnotation class. Most of the code is from Craig, i just added the geometryType property:

//  Created by Craig on 8/18/09.
//  Copyright Craig Spitzkoff 2009. All rights reserved.
//
 
#import "GeometryAnnotation.h"
 
@implementation GeometryAnnotation
@synthesize coordinate = _center;
@synthesize points = _points; 
@synthesize annotationID;
@synthesize geometryType;
 
-(id) initWithPoints:(NSArray*) points withGeometry:(GeometryType)geomType
{
	self = [super init];
 
	geometryType = geomType;
 
	_points = [[NSMutableArray alloc] initWithArray:points];
 
	// create a unique ID for this line so it can be added to dictionaries by this key. 
	self.annotationID = [NSString stringWithFormat:@"%p", self];
 
 
	// determine a logical center point for this line based on the middle of the lat/lon extents.
	double maxLat = -91;
	double minLat =  91;
	double maxLon = -181;
	double minLon =  181;
 
	for(CLLocation* currentLocation in _points)
	{
		CLLocationCoordinate2D coordinate = currentLocation.coordinate;
 
		if(coordinate.latitude > maxLat)
			maxLat = coordinate.latitude;
		if(coordinate.latitude < minLat)
			minLat = coordinate.latitude;
		if(coordinate.longitude > maxLon)
			maxLon = coordinate.longitude;
		if(coordinate.longitude < minLon)
			minLon = coordinate.longitude; 
	}
 
	_span.latitudeDelta = (maxLat + 90) - (minLat + 90);
	_span.longitudeDelta = (maxLon + 180) - (minLon + 180);
 
	// the center point is the average of the max and mins
	_center.latitude = minLat + _span.latitudeDelta / 2;
	_center.longitude = minLon + _span.longitudeDelta / 2;
 
	NSLog(@"Found center of new Annotation at %lf, %ld", _center.latitude, _center.longitude);
 
	return self;
}
 
-(MKCoordinateRegion) region
{
	MKCoordinateRegion region;
	region.center = _center;
	region.span = _span;
 
	return region;
}
 
-(void) dealloc
{
	[_points release];
 
	[super dealloc];
}
 
@end

Now that we have a way to draw a line/polygon as a custom MKAnnotationView, we need a custom TouchView (GeometryTouchView) which could accept the touch events.

For example, if the user wants to draw a line geometry, the GeometryTouchView would accept touch events from the user and add a point as a PointAnnotation in the Map. Succeeding points would be added to an array. For every point added, the MKAnnotationView drawRects method connects the points to produce a line. The MKAnnotationView is now added to the map.

Once the geometry is added as an annotation, the custom TouchView is hidden. This way we have access (panning/zooming) to the mapview. If we make a pan or a zoom, the region changes, thus we need to redraw the shape of the annotation again.

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
	if(currentAnnotationView != nil){
		NSLog(@"regionWillChangeAnimated");
 
		currentAnnotationView.hidden = YES;
	}
}
 
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
	if(currentAnnotationView != nil){
		NSLog(@"regionDidChangeAnimated");
 
		currentAnnotationView.hidden = NO;
		[currentAnnotationView regionChanged];
	}
}
-(void) regionChanged
{
	NSLog(@"Region Changed");
 
	// move the internal line view. 
	CGPoint origin = CGPointMake(0, 0);
	origin = [_mapView convertPoint:origin toView:self];
 
	_internalView.frame = CGRectMake(origin.x, origin.y, _mapView.frame.size.width, _mapView.frame.size.height);
	[_internalView setNeedsDisplay];
 
}

The resulting image is now:
polygon_small_on_top_of_mapview.png

(Download the DrawMap.zip code.) – old. This is for iOS < 4

Categories: iphone Tags:

iPhone Note #20:Integrating Mapserver/TileCache to RouteMe

January 14th, 2010 rupert 2 comments

Below is a summary of how I was able to implement Mapserver, TileCache and Route-Me iPhone Mapping Framework.

1. Assuming you have a working Mapserver/TileCache setup. Take note of the ff parameters: resolution and bbox. Below is my tilecache.cfg:

 61 [mapserver_australia_3857]
 62 type=MapServerLayer
 63 mapfile=/Users/rupert/projects/pelicancorp/DMOB/trunk/map/australia_3857.map
 64 layers=all
 65 extension=jpg
 66 bbox=-20037508.34, -20037508.34, 20037508.34, 20037508.34
 68 maxResolution=156543.033928041
 70 levels=20
 71 srs=EPSG:3857
 72 tms_type=google
 73 extent_type=loose
 74 spherical_mercator=true

2. Grab the RMGenericMercatorWMSSource from http://groups.google.com/group/route-me-map/browse_thread/thread/b58fa1d20cf15823/e30c42d9c90a8170?lnk=gst&q=Generic#e30c42d9c90a8170

3. By default without any changes, the RMGenericMercatorWMSSource could display Mapserver WMS Tiles. This is possible by passing an NSDictionary *parameters, which contains arrayValues and arrayKeys for creating an http 256×256 image request to the http://127.0.0.1/cgi-bin/mapserv binary.

//This would request to http://127.0.0.1/cgi-bin/mapserv
NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"/path-to/australia_3857.map", @"all", @"png", @"EPSG:3857",nil];
NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"MAP", @"LAYERS", @"FORMAT", @"SRS", nil];
NSDictionary *wmsParameters = [[NSDictionary alloc] initWithObjects: arrayValues forKeys:arrayKeys ];

Resulting http requests to the mapserv binary:

http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&SRS=EPSG:3857&REQUEST=GetMap&SERVICE=WMS&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&FORMAT=png&VERSION=1.1.1&WIDTH=256&HEIGHT=256&BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000

Note that the bbox values does not contain decimal places. Nevertheless, it still works on Mapserver.

4. Now, assuming we have a valid tilecache running. And it is tested from browser, i.e http://127.0.0.1/map/tilecache_3857.html, open up firebug to see the requests. Below is a sample…

http://192.168.1.193:81/tilecache/tilecache.py?LAYERS=australia_3857&FORMAT=jpg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A3857&BBOX=16123932.497377,-4539747.9811239,16143500.376618,-4520180.1018829&WIDTH=256&HEIGHT=256

But it seems, route-me is not supplying the bbox values accurately, as the decimal values is truncated (BBOX=16828376.000000,-4006523.000000,16833268.000000,-4001631.000000) from our previous request(3).

After changing the wmsParameter values to create a tilecache request, I noticed that route-me is not displaying the tiles correctly.

//Testing for Windows:TileCache - ?
NSArray *arrayValues = [[NSArray alloc] initWithObjects:@"australia_3857", @"png", @"EPSG:3857",nil]; //for WIndows
 
//TileCache URL Parameters:
NSArray *arrayKeys = [[NSArray alloc] initWithObjects:@"LAYERS", @"FORMAT", @"SRS", nil];

5. The workaround is to use “double” instead of “floats” in the RMGenericMercatorWMSSource. You can download the zip from RMGenericMercatorWMSSource.zip. I have modified “initialResolution” and “originShift” to both use “double”.

typedef struct {
	double x;
	double y;
} CGDoublePoint;
 
typedef struct { 
	CGDoublePoint ul; 
	CGDoublePoint lr; 
} CGXYRect;

Afterwards, I copied the maxResolution and bbox (tilecache) and specified it for the initialResoultion and originShift respectively.

#import "RMGenericMercatorWMSSource.h"
 
CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; 
CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180/ M_PI;}; 
 
@implementation RMGenericMercatorWMSSource
 
-(id) initWithBaseUrl:(NSString *)baseUrl parameters:(NSDictionary *)params
{ 
	if (![super init]) 
		return nil; 
 
	// 156543.03392804062 for sideLength 256 pixels 
	// initialResolution = 2 * M_PI * 6378137 / [self tileSideLength];
	// specify here whatever the resolution is from tilecache.
	initialResolution = 156543.033928041;
 
	// 20037508.342789244 
	//originShift = 2 * M_PI * 6378137 / 2;
	//originShift = 20037508.342789244f;
	// specify here whatever the bbox is from tilecache.
	originShift = 20037508.32;
 
	NSLog(@"test initialResolution:%f originShift:%f", initialResolution, originShift);
 
	// setup default parameters
	// use official EPSG:3857 by default, user can override to 900913 if needed.
	wmsParameters = [[NSMutableDictionary alloc] initWithObjects:[[[NSArray alloc] initWithObjects:@"EPSG:3857",@"image/png",@"GetMap",@"1.1.1",@"WMS",nil] autorelease] 
											  forKeys:[[[NSArray alloc] initWithObjects:@"SRS",@"FORMAT",@"REQUEST",@"VERSION",@"SERVICE",nil] autorelease]];
	[wmsParameters addEntriesFromDictionary:params];
 
	// build WMS request URL template
	urlTemplate = [NSString stringWithString:baseUrl];
	NSEnumerator *e = [wmsParameters keyEnumerator];
	NSString *key;
	NSString *delimiter = @"";
	while (key = [e nextObject]) {
		urlTemplate = [urlTemplate stringByAppendingFormat:@"%@%@=%@",
					   delimiter,
					   [[key uppercaseString] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], 
					   [[wmsParameters objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
		delimiter = @"&";
	}
	int sideLength =  [self tileSideLength];
	urlTemplate = [[urlTemplate stringByAppendingFormat:@"&WIDTH=%d&HEIGHT=%d",sideLength,sideLength] retain];
	return self;
}
 
// implement in subclass?
-(NSString*) uniqueTilecacheKey
{
	return @"AbstractMercatorWMSSource";
}
 
-(NSString *)shortName
{
	return @"Generic WMS Source";
}
-(NSString *)longDescription
{
	return @"Generic WMS Source";
}
-(NSString *)shortAttribution
{
	return @"Generic WMS Source";
}
-(NSString *)longAttribution
{
	return @"Generic WMS Source";
}
 
-(float) minZoom
{
	return 1.0f;
}
-(float) maxZoom
{
	return 19.0f;
}
 
// Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:3857 
-(CGPoint) LatLonToMeters: (CLLocationCoordinate2D) latlon 
{ 
	CGPoint meters; 
	meters.x = latlon.longitude * originShift / 180; 
	meters.y = (log( tan((90.0 + latlon.latitude) * M_PI / 360.0 )) / (M_PI / 180.0)) * originShift / 180; 
	return meters; 
}
 
//Converts XY point from Spherical Mercator EPSG:3857 to lat/lon in WGS84 Datum 
-(CLLocationCoordinate2D) MetersToLatLon: (CGPoint) meters 
{ 
	CLLocationCoordinate2D latlon; 
	latlon.longitude = (meters.x / originShift) * 180.0; 
	latlon.latitude = (meters.y / originShift) * 180.0; 
	//latlon.latitude = - 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); 
	latlon.latitude = 180 / M_PI * (2 * atan( exp( latlon.latitude * M_PI / 180.0)) - M_PI / 2.0); 
	return latlon; 
} 
 
// Converts pixel coordinates in given zoom level of pyramid to EPSG:3857 
-(CGDoublePoint) PixelsToMeters: (int) px PixelY:(int)py atZoom:(int)zoom 
{ 
	double resolution = [self ResolutionAtZoom: zoom]; 
	CGDoublePoint meters; 
	double x = (px * resolution - originShift); 
	double y = (py * resolution - originShift); 
 
	meters.x = x;
	meters.y = y;
 
	NSLog(@"px: %d py: %d resolution: %f originShift: %f x: %f y: %f", px, py, resolution, originShift, x, y);
 
	return meters; 
} 
 
-(NSString*) tileURL: (RMTile) tile 
{ 
	//RMLatLongBounds tileBounds = [self TileLatLonBounds:tile];
	// Get BBOX coordinates in meters
	CGXYRect tileBounds = [self TileBounds:tile];
 
	NSString *url = [urlTemplate stringByAppendingFormat:@"&BBOX=%f,%f,%f,%f",
					 tileBounds.ul.x,
					 tileBounds.lr.y,
					 tileBounds.lr.x,
					 tileBounds.ul.y];
 
	NSLog(@"Tile %d,%d,%d yields %@",tile.zoom, tile.x, tile.y, url); 
 
	return url; 
} 
 
 
//Returns bounds of the given tile in EPSG:3857 coordinates 
-(CGXYRect)  TileBounds: (RMTile) tile 
{
	int sideLength =  [self tileSideLength];
 
	int zoom = tile.zoom;
	long twoToZoom = pow(2,zoom);
	CGXYRect tileBounds; 
	tileBounds.ul = [self PixelsToMeters: (tile.x * sideLength) 
								  PixelY: ((twoToZoom-tile.y) * sideLength) 
								  atZoom: zoom ]; 
	tileBounds.lr = [self PixelsToMeters: ((tile.x+1) * sideLength) 
								  PixelY: ((twoToZoom-tile.y-1) * sideLength) 
								  atZoom: zoom];
	return tileBounds; 
} 
 
//Resolution (meters/pixel) for given zoom level (measured at Equator) 
-(double) ResolutionAtZoom : (int) zoom 
{ 
	return initialResolution / pow(2,zoom); 
} 
 
@end

6. Download TileCache_RouteMe.zip Note: You need a valid mapserver and tilecache running.

iPhone Note #19: Route-Me: Opensource mapping for the iphone

December 8th, 2009 rupert Comments off

Route-Me is an opensource mapping api similar to the MapKit.framework. The main project resides in googlecode (http://code.google.com/p/route-me/).

Part 1: Making the Sample Apps work.

1. Download the code from svn.
svn co http://route-me.googlecode.com/svn/trunk/ route-me

2. In the route-me directory, you would have 3 directories:
- MapView – this contains the mapping framework
- Proj4 – projection library.
- samples

3. Build MapView. I encountered some problems here, particularly this specific issue: Cannot build on 3.1 using iphone simulator.

How to make the build? Now, at the time of this writing, I have XCode3.1.4 and iPhone 3.0, 3.1, 3.1.2. Edit the project settings so that the “build” and “release” will point to use 3.0. If you encounter a problem, it is best to “Clean All”, remove the build directory, then “Build” again. To verfiy if you have a successful build, notice that libMapView.a was created.

routeme-libMapView.png

Note: Notice here that I have libMapView.a both in Debug-iphoneos/ and Debug-iphonesimulator/

No need to build Proj4 as long as it is sitting on the same directory where MapView is.

4. To test route-me, we can study from the samples directory. Let’s build samples/SampleMap. Notice that SampleMap has a reference to MapView.xcodeproj.

routeme-samplemap.png

Similarly, we can build this using 3.0 both in the simulator and device.

routeme-samplemap-ok.jpg

Part 2: Integrating with your own project.

Reference: http://code.google.com/p/route-me/wiki/EmbeddingGuide

1. Create a view-based project: MyRMSampleMap.

2. Project -> “Add To Project”. Make the reference type as “Relative to Project”.

routeme-referencing-mapview.jpg

3. Configure your project to have a direct dependency with MapView.xcodeproj. Follow the instructions exactly as stated from the EmbeddingGuide.

routeme-ref-dd.jpg

Click on the “Build” tab in your target’s info window.

Change the Configuration popup to read “All Configurations”, so that your changes can be made just once.

Find “Header Search Paths” under “Search Paths” (shortcut: type “header” in the search box at upper right of the target info window). Double-click on the ‘Header Search Paths’ text and add the path to the MapView directory contained in the route-me project located on your file system. (Note that if there are any spaces in the path, enclose the entire entry with “”. If the MapView project folder is placed next to your project’s project folder on the file system, you would have the following in the path: “../MapView”.

Check the ‘Recursive’ box and click Ok.

routeme-searchpaths.jpg

4. Make a test “Build”.

5. Add a reference to:
- QuartzCore.framework
- libsqlite3.dylib

6. Let’s add our mapview in the interface: MyRMSampleMapViewController.h

#import <UIKit/UIKit.h>
#import "RMMapView.h"
 
@interface MyRMSampleMapViewController : UIViewController {
	RMMapView *mapview;
}
 
@property(nonatomic, retain) RMMapView *mapview;
 
@end

7. In the implementation, add this snippet.

@synthesize mapview;
...
- (void)viewWillAppear:(BOOL)animated{
	mapview = [[RMMapView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 460.0f)];
	[mapview setBackgroundColor:[UIColor blackColor]];
 
	[self.view addSubview:mapview];
}

Download: MyRMSampleMap.zip

Categories: iphone Tags:

iPhone Note #18: Integrating Ads (AdMob) on your iPhone App

December 7th, 2009 rupert 2 comments

Basic Info

What is common with these ad platforms?
Each application is offered a unique id which needs to be specified during development.

What is publishing?
Publishing allows the developer to display (publish) ads on the native application.

What is CPM?
Impressions per thousand. Normally ad networks pays $1 for every 1000 CPM. So you need 1000 page views (not clicks) to earn a buck.

What is the fillrate?
The percentage of impressions served based on total requests for ads. This is number to look for when you get blank ads. How many of my clients gets an ad impression? I had 60%

What is the Click Through Rate (CTR)?
Out of 60% of my clients who had ad impressions, 6.18% of them clicked the ad. The more they get clicked, the higher your revenue.

Admob

Offers both publishing and analytics. Currently offers both iPhone and Android. Able to publish ads for native app and mobile websites. This is my preferred ad network of choice as it is easier to setup than the others. And just recently, it was swallowed by Google, whether that is good or bad for us, we have yet to know.

AdMob Iphone SDK Integration

1. After signing up/registering to Admob you need to add your application in “Sites & Apps” tab (2nd tab from the left).

2. Click on “Add Site/App” and you would be presented with what kind of site or app type (Mobile Web, Iphone App, Iphone Web and Android App). Select “Iphone App” and fill out the details..

3. If you go to “Sites & Apps”, there’s a link “Setup” just under the Quick Links. You could download a sample AdMob application there wherein your publisherId is already built-in. Here’s a sample (admob_iphone_sdk_20091119.tar.gz). Note that AdMob upgrade their code, notice the dates (20091119)? So it’s good practice to read the CHANGELOG.

4. Note your publisherId.

#pragma mark Admob delegate methods
- (NSString *)publisherId {
	return @"a14ac98028663b0"; // bayanihan
}

5. Make the sample application from AdMob run first before integrating your app.

// To receive test ads rather than real ads...
- (BOOL)useTestAd {
	return YES; //change this to NO later..
}

6. Normally, the problems/quirks I experience when integrating a 3rd Party Project are framework references. So, here’s mine:

admob_frameworks.png

Note: As of ver 20091119, I added MessageUI.framework, AudioToolbox.framework, MediaPlayer.framework. Prior to that build (200906xx), I don’t have those. If you are compiling against iPhoneOS version 3, then you would need to reference libAdMObDevice3_0.a and libAdMobSimulator3_0.a (to run it in the simulator–but you would take this reference off when making an actual build)

admob-iphoneos3.png

7. Now, that both the testAd (YES) is working, it might take you a few hours before the real ad comes in. Be patient. :)

8. Integrate to your project. AdMob provided detailed examples on how to integrate depending on your needs–Interface Builder (IB), Programmatically or TableView. Personally, I have used both IB and TableView and it works flawlessly.

9. I dont get an ad? I get an Ad? Which one? Check out the AdMobDelegate Methods.

- (void)didReceiveAd:(AdMobView *)adView {
	NSLog(@"AdMob: Did receive ad");
}
 
- (void)didFailToReceiveAd:(AdMobView *)adView {
	NSLog(@"AdMob: Did fail to receive ad");
 
	//[self createMobclix];
	//[bannerCell setNeedsLayout];
}

10. Now once you have AdMob going, you want to be more efficient and get higher fill rates. You can checkout other Ad Platforms (Mobclix, Adwhirl). Notice on step 9 above, that if I don’t get an ad from AdMob, I fire up an add from MobClix.

Categories: iphone Tags:

iPhone Note #17: Displaying a custom view controller from a UITextField

November 2nd, 2009 rupert Comments off

Problem: I want my textFieldSearchAddress to display a seperate viewcontroller.

textfield.png

Short Answer: Hide the keyboard by using

[textField resignFirstResponder]

then show the view controller.

1. Implement a UITextFieldDelegate.

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
	[textField resignFirstResponder];
	[self show];
	return NO;
}

Be careful with the BOOL return of textFieldShouldBeginEditing. From the docs: “YES if an editing session should be initiated; otherwise, NO to disallow editing.”

2. Show the view controller.

- (IBAction)show{
	NSLog(@"show");
 
	AddressViewController *addressViewController = [[AddressViewController alloc] initWithNibName:@"AddressViewController" bundle:nil];
	UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:addressViewController];
	[self presentModalViewController:nav animated:YES];
 
	[addressViewController release];
	[nav release];
 
}

Download SimpleIB-textfield_custom_view_controller.zip

Categories: iphone Tags: