Archive for January, 2010

30 JanHow to make locate me button like in Maps.app

It looks like we cannot make Locate Me button similar to original one from Maps.app. I mean an easy and with public API way. But there is a simple way to simulate it and make clear for users. Paste this snippet to xCode and connect several IBOutlets and IBActions.

IBOutlet UIBarButtonItem *findMeBarButton; IBOutlet UIBarButtonItem *activityBarButton;
- (void)showFinding{ if (!activityBarButton) {  UIView *customView = [[UIView alloc]
initWithFrame:CGRectMake(0, 0, 37, 30)];   UIActivityIndicatorView *actIndicator =
[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: 
UIActivityIndicatorViewStyleWhite];
[actIndicator startAnimating];  [actIndicator setCenter:customView.center];
[customView addSubview:actIndicator];   activityBarButton = [[UIBarButtonItem alloc]
initWithCustomView:customView][actIndicator release];  [customView release]; } 
[self.navigationItem setLeftBarButtonItem:activityBarButton animated:YES];} -
(void)hideFinding{ self.navigationItem.leftBarButtonItem = findMeBarButton;}

21 JanBackground asynchronous downloads or other jobs

In current development I need to synchronize tons of data with a server. The big “Synchronize” button in the settings or somewhere else isn’t a good solution, because user starts the application to use, but wait any system jobs. So the background synchronization is more better. I found the easiest way to do it and want to share a code snippet with you.


Run method in backgorund right from UIApplicationDelegate:

- (void) startBackgroundSynch{ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
BackgroundSynch *synch = [[[BackgroundSynch alloc] init] autorelease]; [synch startDownloading];
 [pool release];} - (void)applicationDidFinishLaunching:(UIApplication *)application 
{ [self performSelectorInBackground:@selector(startBackgroundSynch) withObject:nil];
 // Override point for customization after app launch
    [window addSubview:viewController.view];    [window makeKeyAndVisible];}

The BackgroundSynch must create NSURLConnection and receives NSURLResponse(s). But the problem you’d face with is NSURLConnection’s events (didReceiveResponse, didReceiveData, connectionDidFinishLoading) are never called. And that’s a small trick.

@interface BackgroundSynch : NSObject{ NSMutableData *responseData; BOOL finished;}
 - (void) startDownloading;@end
#import "BackgroundSynch.h" @implementation BackgroundSynch 
- (void) startDownloadingWithUrl:(NSString*)urlString{ NSLog(urlString); NSURL *url
= [NSURL URLWithString:urlString]; NSMutableURLRequest *request =  [NSMutableURLRequest
requestWithURL:url                cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30.0][request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]} -
(void) startDownloading{ [UIApplication sharedApplication].networkActivityIndicatorVisible =
 YES; NSString *urlString = @"http://www.archive.org/download/BettyBoopCartoons/Betty_Boop_More_
Pep_1936_512kb.mp4"; [self startDownloadingWithUrl:urlString]// THAT'S A TRICK >> finished =
 NO; while(!finished) {  [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:
[NSDate distantFuture]]; }} ////// RECEIVING DATE FROM SERVER/// - (void) connection:(NSURLConnection
 *)connection didReceiveResponse:(NSURLResponse *)response{ responseData = [[NSMutableData alloc] init];}
 - (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)aData{ [responseData
appendData:aData]; NSLog(@"DATA: %d", [responseData length]);} - (void) connectionDidFinishLoading:
 (NSURLConnection*) connection { [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 [responseData release], responseData = nil; finished = YES;} -(void) connection:(NSURLConnection *)
connection didFailWithError:(NSError *)error { [responseData release], responseData = nil;
 [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; finished = YES;} @end

Hope you were careful enough to find an while-loop depending at the end of “startDownloading”. It depends on boolean variable which tells if data is downloading or downloaded(failed).

Now user can normally work in GUI and data automatically synchronizes on background.

13 JanForismatic is #1 on the Russian App Store

Forismatic (US link) is my free client for forismatic.com – a base of Russian famous quotes. The forismatic’s motto is ”the most inspiring quotes of mankind” and all they really inspire.

Today it’s on the first place on the Russian App Store. Congratulations, guys! It’s awesome position!

05 JanRouteMe is alternative map control to Google Maps from iPhone SDK [part 2]

Have time for a little extention of simple RouteMe quide. Let’s add a user location finding.

Create find button (code+design)

  1. add “IBOutlet UIBarButtonItem *findmeBarButton;”
  2. add empty method “- (IBAction) startFinding”
  3. add “Find me” button in the IB of the main view
  4. connect the button with IBOutlet and “startFinding” IBAction


Core Location (interface)

  1. import “CoreLocation/CoreLocation.h”
  2. add CLLocationManagerDelegate to the interface
  3. add “CLLocationManager* locationManager;”
  4. and we will need some methods in a moment
  5. now the interface looks like:

#import "UIKit/UIKit.h"#import "RMMapView.h"#import "CoreLocation/CoreLocation.h" @interface RouteMeSourceSelectionViewController : UIViewController<rmmapviewdelegate, cllocationmanagerdelegate="" uipickerviewdatasource,="" uipickerviewdelegate,="">{ IBOutlet RMMapView *mapView; IBOutlet UIPickerView *mapSourcePicker; IBOutlet UIBarButtonItem *mapSettingsBarButton; IBOutlet UIBarButtonItem *findmeBarButton;  CLLocationManager* locationManager;} - (IBAction) showMapsSettings;- (IBAction) startFinding;- (void) stopFinding; - (void) showFinding;- (void) hideFinding; @end

sorry it’s copied with some errors. It’s better to copy code from sources.

Core Location (code)

The first thing is to create and start/stop a location manager instance:

- (IBAction) startFinding{ if (findmeBarButton.style == UIBarButtonItemStyleBordered) {   if (!locationManager)  {   locationManager = [[CLLocationManager alloc] init];   locationManager.delegate = self;   locationManager.distanceFilter = 10; // 1000 = kilometer   locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;  }   [self showFinding];  [locationManager startUpdatingLocation];  NSLog (@"Locating started"); } else  [self stopFinding];} - (void) stopFinding{ [locationManager stopUpdatingLocation]; [self hideFinding]; NSLog (@"Locating stopped");}

When Finding is on we change the title and illuminate the FindMeButton, and vice versa. These methods make this and some other GUI changes:

- (void)showFinding{ [findmeBarButton setTitle:@"Finding..."]; [findmeBarButton setStyle: UIBarButtonItemStyleDone]; [mapSettingsBarButton setEnabled:NO];} - (void)hideFinding{ [findmeBarButton setTitle:@"Find Me"]; [findmeBarButton setStyle: UIBarButtonItemStyleBordered]; [mapSettingsBarButton setEnabled:YES];}

Similarly hide/show “findmeBarButton” when show/hide the maps’s settings: “[findmeBarButton setEnabled:!findmeBarButton.enabled];”

At least we need to implement location protocol methods: show message if any error and show place on the map when location is found. Make maps’s zoom depend of found location’s accuracy.

- (void)locationManager:(CLLocationManager *)manager    didUpdateToLocation:(CLLocation *)newLocation           fromLocation:(CLLocation *)oldLocation{ [mapView.contents moveToLatLong:newLocation.coordinate]if([newLocation horizontalAccuracy] &gt; 1000) {  if([newLocation horizontalAccuracy] &gt; 10000)   [mapView.contents setZoom:6];  else if([newLocation horizontalAccuracy] &lt;= 10000 &amp;&amp; [newLocation horizontalAccuracy] &gt; 5000)   [mapView.contents setZoom:9];  if([newLocation horizontalAccuracy] &lt;= 5000 &amp;&amp; [newLocation horizontalAccuracy] &gt; 1000)   [mapView.contents setZoom:12][self showFinding]; } else if([newLocation horizontalAccuracy] &lt;= 1000 &amp;&amp; [newLocation horizontalAccuracy] &gt;= 0) {  [mapView.contents setZoom:16];  [self stopFinding]; }} - (void)locationManager:(CLLocationManager *)manager     didFailWithError:(NSError *)error{ [self stopFinding]; UIAlertView *errorAlert = [[UIAlertView alloc]          initWithTitle: @"Location not found"          message:nil          delegate:nil          cancelButtonTitle:@"OK"          otherButtonTitles:nil]; [errorAlert show]; [errorAlert release];}

This sample is a part of my real application - Meeting Point. It’s free to download, try it.