Archive for the 'cocoa touch snippet' Category

27 MayGrand Central Dispatch magic – async image demo

The Grand Central Dispatch (GCD) is a big topic. The first thing I wanted to try when I started to learn it a few months ago is set of asynchronous operations like image downloads. Check out my old sample how to get Asynchronous UIImage from web or this one if you need to download a really big file in the background on the app launch.

Current sample is for situation when you need to download a set of files, so you must have a queue. The magic of GCD is that it manages queues automatically depending of available resources and their priorities. GCD does that efficiently because of low level implementation and Bloc

Demo

  • has a predefined set of urls to download
  • creates a GCD queue and starts to download images
  • shows each image as soon as it’s available with appearing–dissappearing animation

Implementation

Тhe demo result is nice Category UIImageView+DispatchLoad:

Note, this is just a sample how GCD works with queues and asynchronous operations. Don’t use it in production because it’s usually need to cancel and re-prioritize network downloads. GCD approach is nice for local operations like saving a big file to disk. GCD lets us to keep UI smooth while heavy operations – that’s the main point.

04 MarSmart logging for unsatisfied with NSLog

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial; min-height: 16.0px}

Logging is very useful and powerful tool. All iOS programmers knows about such great method as NSLog(NSString *format);

But there is one problem on active usage of that method – usually you don’t need to logging something in Release version of a product, because logging need resources.
Let’s take a look on two solutions to avoid.
But to start a little prerequisites.
1. Open your project settings (Project -> Edit Project Settings).
2. Go to the ‘Build‘ tab.
3. Make ‘Debug‘ as currently selected configuration.
4. Set ‘DEBUG=1‘ to configure Preprocessor Macros.p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}

First solution
1. Add the following macro into *Prefix.pchfile

#ifdef DEBUG    #define LOG(...) NSLog(__VA_ARGS__)#else    #define LOG(...)#endif

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial; min-height: 16.0px}

This allow us to use LOG() macro from whatever place in the programm instead of NSLog() using and also this disable logging for Release versions.
Simple and convenient. But this method has one drawback. What if the project is large and the information it logged a lot? If so you will get much unnecessary info in your console. But this is not a problem, let’s go to the second solution.
Second solution
The idea is to split all info to a different urgency levels and allow to configure which levels we need to write into log. Let’ go.
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}

1. Add new class into the project and name it ‘Log‘.
2. Add macros for different urgency levels into Log.h
  LOG_LEVEL_MOST_IMPORTANT – for high priority information
  LOG_LEVEL_NORMAL  – for normal priority information
  LOG_LEVEL_COMMON  – other information that doesn’t need much attention (debug info etc.)

#define LOG_LEVEL_MOST_IMPORTANT 1#define LOG_LEVEL_NORMAL 2#define LOG_LEVEL_COMMON 3

 

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}

3. Add method definition for logging
+ (Void) log: (NSString *) message level: (short) level;

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}

Log.hfinal version should looks like:

#define LOG_LEVEL_MOST_IMPORTANT 1#define LOG_LEVEL_NORMAL
2#define LOG_LEVEL_COMMON 3 @interface Log : NSObject {} +
(void)log:(NSString *)message level:(short)level;

@end
4. Add two directives into Log.m 
LOG_LEVEL_NOTHING - show nothing in console
CURRENT_LOG_LEVEL - show information with current detalisation level (is equal one of LOG_LEVEL_xxx macros). This directive must be wrapped by #ifdef DEBUG … #else … #endif to avoid logging in Release versions.
#define LOG_LEVEL_NOTHING 0 #ifdef DEBUG    
#define CURRENT_LOG_LEVEL LOG_LEVEL_ MOST_IMPORTANT#else
#define CURRENT_LOG_LEVEL LOG_LEVEL_NOTHING#endif
5. Implement method for logging

+ (void)log:(NSString *)message level:(short)level { if (CURRENT_LOG_LEVEL >= level)
NSLog(@"%@", message);}

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Helvetica; min-height: 17.0px}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}

Pretty simple.. huh?
6. The last step – add import of Log.h into *Prefix.pch that allow us to use our newly created logging method
#import “Log.h”

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}

Now you can use
[Log log:@"test message" level:LOG_LEVEL_COMMON];
to log information with normal urgency level, or
[Log log:@"Warning! Server not response!" log:LOG_LEVEL_MOST_IMPORTANT];
to log most important information, like problems with connection to a server or whatever.

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Arial; min-height: 16.0px}

For tests in different versions you can set CURRENT_LOG_LEVEL = LOG_LEVEL_MOST_IMPORTANT.
This allow to write most important information into the console.
Of course, this solution is not as elegant as using of the LOG() macro, but it is quite flexible.

26 NovFile Sharing from App to iTunes is too easy

The idea is to give to users an access to files from applications. For example, a file is downloaded or new one is created on the iPad and user wants to use it on the desktop then.


Let’s do that:

  1. Set “Application supports iTunes file sharing” on on your project’s settings plist. It could also be named with “UIFileSharingEnabled“.
  2. Add a file and remember its name. I’ve added “Alterplay.gif” – Alterplay logo from our Basecamp account.

3. Code

Copy file to “/Documents” on application run:

- (BOOL)application:(UIApplication *)  application
didFinishLaunchingWithOptions: (NSDictionary *)
launchOptions
{ // Override point for customization after application launch.  // file sharing trying
{  NSString *fileName = @"Alterplay.gif";  NSFileManager *fileManager =
[NSFileManager defaultManager];
NSError *error;  NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory,NSUserDomainMask, YES);  NSString *documentsDirectory =
[paths objectAtIndex:0];NSString *documentDBFolderPath = [documentsDirectory
stringByAppendingPathComponent:fileName];if
(![fileManager fileExistsAtPath: documentDBFolderPath]
 )   {    NSString *resourceDBFolderPath =
[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
[fileManager copyItemAtPath:resourceDBFolderPath toPath:documentDBFolderPath
 error:&error];  } } [self.window makeKeyAndVisible]return YES;}
To prove:
  • run on your iPhone
  • close the app
  • open iTunes and go to the apps section to the bottom
  • select “FileSharingSample” and see/copy the file

 

27 AugSave multiple wallpapers to Photos Album, UIImageWriteToSavedPhotosAlbum

Sure you know about method to copy an image to user photo library:

void UIImageWriteToSavedPhotosAlbum(UIImage
*image, id completionTarget, SEL completionSelector,
void *contextInfo);

but if you try to save multiple files at once

for (int i=1; i<=10; ++i){  UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);}

you’ll have every second/third file saved or even nothing will be saved. To investigate what’s wrong it’s required to set complete selector to UIImageWriteToSavedPhotosAlbum:

- (void)image:(UIImage *) image didFinishSavingWithError: (NSError *)error
contextInfo:(void *)contextInfo;

looks like this:

for (int i=1; i<=10; ++i){ UIImageWriteToSavedPhotosAlbum(image,
self, @selector(savedPhotoImage : didFinishSavingWithError:contextInfo:), nil);} -
(void) savedPhotoImage:(UIImage*)image
didFinishSavingWithError:(NSError *)error contextInfo:
(void *)contextInfo{ NSLog(@"%@",
[error localizedDescription]);}

and then you’ll see an error description “write busy” at the log. System just unable to copy files so fast. Yes, you use just a mobile device with limited, but miraculous abilities ;) It isn’t 12 Core Mac Pro.

The solution is to copy photos one by one:

  1. create an array of image names, e.g. wallpapers
  2. copy one
  3. catch complete selector
  4. goto #2

- (void) saveWallpapers{ // fill array of names wallpapers = [[NSMutableArray alloc]
 initWithCapacity:10]; for (int i=1; i<=10; ++i) {  [wallpapers addObject:imageName];
}  // start copying [self saveNextWallpaper];} - (void) saveNextWallpaper{
// copy a wallpaper and remove from the array if (wallpapers && wallpapers.count > 0)
{  UIImage *image = [UIImage imageNamed:[wallpapers lastObject]];
UIImageWriteToSavedPhotosAlbum(image,
self, @selector(savedPhotoImage:didFinishSavingWithError:contextInfo:), nil);
[wallpapers removeLastObject]; }} - (void) savedPhotoImage:(UIImage*)image
didFinishSavingWithError: (NSError *)error contextInfo: (void *)contextInfo{
// if still any wallpaper if (wallpapers)
{  [self saveNextWallpaper]if (wallpapers.count == 0)
{   // finish copying   UIAlertView  *errorAlert = [[UIAlertView alloc]
nitWithTitle:nil            message:@"Wallpapers are stored to Photos Album"
delegate:nil            cancelButtonTitle:@"ОК"
otherButtonTitles:nil];   [errorAlert show];   [errorAlert release];
[wallpapers release],
wallpapers = nil;  } }  NSLog(@"%@", [error localizedDescription]);}

02 AugUser custom color theme for applications (v0.1)

The idea is to create simple and intuitive UI for user custom themes. For example, let user to define own colors of text labels and background. It’s need in some of reading applications.

Should it be interesting for all users? I don’t know. It could seem too tech/geeky, but user always has a default theme and nobody is forced to customize. Assuming not more 25% of users will need and take advantage of it. Since I’m going to integrate it in Forismatic, I’ll be able to post useful statistics some day.

What I mean a good UI is

  • intuitive – user sets a color by tapping and immediately sees a result to understand if he likes it
  • simple – user sets a color by one finger, no digit typing, quick
Color model

That’s an important quesiton, because a model defines a way of thinking. The more natural model is, the faster human adapts. “Habits are what we are.” ©

For example, try to imagine how user can think: “I want a green. A little brighter and then a little lighter, ah no.. less lighter. Yeah, that’s it, but add more saturation.”. That’s the way I search colors, others do similar way. But try to simulate it with the RGB color model. Yes, it’s hard, because it’s the way computers think. You should have experience in converting familiar logic to RGB. Most users don’t have.

Nobody said we should use RGB. Look how much models exist. I think, the most comfortable is Hue-Saturation–Brightness model (HSV:HSB). The article too long, so shortly HSV is class of models designed in the mid-1970s as the closest to human color perception. HSV is hue–saturation–value, where the last attribute may be lightness or brightness or other. For example, RGB and CMYK define any color as a combination of predefined colors, but color components in HSV model display more familiar information, like: what is a color? how much is it saturated? is it light or dark? That’s what I was searching for. Now let’s try to use it. I’ve chosen HSB for experiments. Sample applications from wikipedia:

Project

Technically all are more simply than described. We just need to change UIView.backgroundColor according to user input values. Since a color is defined by 3 components, I suggest to:

  • by tapping the screen user defines hue (H) and brightness value (B) values. The X and Y coordinates of a UITouch of the UIView.
  • by tapped moving user still changes H and B.
  • for simplicity, use UISlider to change the last component – saturation (S)
I did it this way, because I found experimentally it’s the most comfortable. You could you try it also.

Preparations

  1. use View-based Application template, name it UserColorizer.
  2. TARGETED_DEVICE_FAMILY = 1,2 (iPhone/iPad)
  3. IPHONEOS_DEPLOYMENT_TARGET = 3.0
  4. Open UIView xib (mine is Colorer.xib) and add UISlider with ValueChanged IBAction and value [0.0,1.0]; UILabel with IBOutlet.
  5. Optionally add an partially transparent UIImage. It adds variety to the interface and helps to understand if you like selected color combination.

Interface

@interface Colorer : UIViewController { IBOutlet UILabel *label;
// color components float hue; float saturation; float brightness;}
-(IBAction) slider_сhanged:(UISlider*)slider; @end
Implementation

  • set default values for components
  • update color. There is a redefined method for this: “[UIColor colorWithHue:...]
  • turn autorotation on. You should align subviews right way in Interface Builder.
  • set saturation when its UISlider changed and then update the background color

- (void) refreshColor{ self.view.backgroundColor = [UIColor colorWithHue:hue
saturation:saturation brightness:brightness alpha:1.0];} – (void)viewDidLoad {
[super viewDidLoad];  hue = 0.5; saturation = 0.5; brightness = 0.5;  [self refreshColor];}
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{ return YES;} – (IBAction) slider_changed:(UISlider*)slider{ saturation =
slider.value; [self refreshColor];}To catch user’s tapping and moving we need to:

  • override two UIViewController’s methods
  • get touch location in the UIView
  • convert location to hue and brightness
  • update color

// return real bounds according to device position- (CGSize) bounds
{ if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
return CGSizeMake(self.view.frame.size.height, self.view.frame.size.width) ;
else  return CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);}
- (void) touched:(NSSet *)touches{ CGSize size = [self bounds]; CGPoint newPoint =
[[touches anyObject] locationInView:self.view];  hue = newPoint.x/size.width; brightness
=  1-newPoint.y/size.height; [self refreshColor];} – (void) touchesBegan: (NSSet *)
touches withEvent:(UIEvent *)event{ [self touched:touches];} – (void) touchesMoved:
(NSSet *)touches withEvent: (UIEvent *) event{ [self touched:touches];}Ok, now we can change a color. Nice! It’s left to implement text coloring.

I suggest to set text color to “1.0-brightness”. Nice looking and simple to implement and use. But there is a problem area when brightness is near 0.5 value. It makes text and background with almost the same color. Again experimentaly I saw that colors are blended from value 0.45 to value 0.60 and matched the best brightness for this interval, which 0.40. It’d be nice to set a small shadow for the text, use the same color with 0.4 alpha.

- (void) refreshColor{ self.view.backgroundColor =
[UIColor colorWithHue:hue saturation:saturation brightness:
brightness alpha:1.0];  float textBrightness; if  (brightness
< 0.45 || brightness > 0.60)  textBrightness = 1.0-brightness;
else  textBrightness =0.4;  label.textColor = [UIColor colorWithHue
:hue saturation: saturation brightness:textBrightness alpha:1.0];
label.shadowColor = [UIColor colorWithHue:hue saturation:saturation
brightness: textBrightness alpha:0.4];}
Demo

That’s all what I want from the first try. Now I think about:

  • how to remove UISlider
  • more area for colors, less for dark
  • somehow to show where user tapped last time
It’s a dual sense when undestand that write an article is three times longer than to code the project. It proves Inot a writer, but a coder ;)

29 JulHelpful primitive #DEFINEs I really like

#define cgp(x,y) CGPointMake(x,y)#define cgpz CGPointMake(0,0)
#define cgf(x,y,w,h) CGRectMake(x,y,w,h)

#define NUM(i) [NSNumber numberWithInt:i]#define NUMF(f) [NSNumber numberWithFloat:
f]// etc if you need, I don’t// this one is MUST HAVE EVER#define IMG(name)
[UIImage imageNamed:name]#define NSTR(patern, value) [NSString stringWithFormat:patern,
value]#define NSTR2(patern, value1, value2) [NSString stringWithFormat:
patern, value1, value2]#define INT2STR(i) [NSString stringWithFormat:@"%d",
i]#define FLT2STR(i) [NSString stringWithFormat:@"%0.2f", f]// etc if you needThe most important reason not to use #DEFINE is code readability, imho. Everyone can define own abbriveations and then get dead code. I mean not an Unreachable code, but code that nobody else can support effectively, even an author after months left. It’s easy to get addicted and generate shorteners for every partially doubled line.

That’s why I propose only helpful #defines I use almost in every project. They look very simple and even obvious. I place them in the precomiled *.pch file to be availabe in all sources.

This helps with coordinates:

This helps with string creation and constants:

Other small objects:

There are 10 more for subviews creation, but it’s more like an approach than promitive shortneners. I’ll describe that later with samples.

I’ve googled a little and there are some nice links on that:

I suppose it helps begginers only. Others can write their experience in comments.

18 JulWhat I’ve already used more than once from iOS 4.0

Want several code snippets on iOS 4.0 features to share.


1. iAds and ADBannerView

In Alterplay we have free apps with US targeting. It’s good possibility to try iAds with that.

Check if there is a ADBannerView and then create it programmatically. You can’t do it in Interface Builder if iOS 3.0-3.1.3 is required to support.

#import

bannerIsVisible = NO;if (NSClassFromString(@”ADBannerView”)){ // create banner banner =
[[ADBannerView alloc] initWithFrame:CGRectZero]; banner.frame = CGRectMake(0, -50, 320,
50); banner.alpha = 0.0; banner.requiredContentSizeIdentifiers = [NSSet setWithObject:
ADBannerContentSizeIdentifier320x50]; banner.currentContentSizeIdentifier =
ADBannerContentSizeIdentifier320x50; [self.view addSubview:banner]; banner.delegate=self;
bannerIsVisible=NO;}

Then just two methods from ADBannerViewDelegate. I show iAds on views with browser, so I
need to move a UIWebView also. ‘TrackPage()’ is Google Analytics function to gather
statistics. I’ll make separate post for it.

- (void)bannerViewDidLoadAd:(ADBannerView *)aBanner{ TrackPage(@”bannerViewDidLoadAd”);
if (!bannerIsVisible) {  [UIView beginAnimations:@"animateBannerAppear" context:NULL];
aBanner.alpha = 1.0;  aBanner.frame = CGRectOffset(aBanner.frame, 0, 50);  webBrowser.
frame = CGRectMake(0, aBanner.frame.size.height, 320, 416-aBanner.frame.size.height);
[UIView commitAnimations];  bannerIsVisible = YES; }} – (void)bannerView:(ADBannerView *)
aBanner didFailToReceiveAdWithError:(NSError *)error{ if (bannerIsVisible) {  [UIView
beginAnimations:@"animateBannerDissappear" context:NULL];  aBanner.alpha = 0.0;  aBanner.
frame = CGRectOffset(aBanner.frame, 0, -50);  webBrowser.frame = CGRectMake(0, 0, 320, 416);
[UIView commitAnimations];  bannerIsVisible = NO; }}

2. inApp SMS and MFMessageComposeViewController

Again, it’s need to support older iPhoneOS, so ‘NSClassFromString’ helps. iPodTouch is
one more case to check. When it’s older iPhoneOS my suggestion is to copy message and
ask user to open SMS app. ‘LOCAL’ is my define for localized text constants. Think,
I’ll share my defines with another post.

+ (void) SendSMSFrom:(UIViewController*)ctrl withText:(NSString*)text{ if
(NSClassFromString(@”MFMessageComposeViewController”) &&
[MFMessageComposeViewController canSendText]) {  MFMessageComposeViewController *picker =
[[MFMessageComposeViewController alloc] init];  picker.messageComposeDelegate =
[Sharer Shared];  picker.body = text;  [ctrl presentModalViewController:picker animated:YES];
[picker release]; } else  {  [UIPasteboard generalPasteboard].string = text;   UIAlertView
*alert = [[UIAlertView alloc] initWithTitle:LOCAL(@”sharing sms title”)
message:LOCAL(@”sharing sms message”)
delegate:[Sharer Shared]              cancelButtonTitle:LOCAL(@”No”)
otherButtonTitles:LOCAL(@”Open”), nil];  [alert show];  [alert release]; } }
- (void)messageComposeViewController:(MFMessageComposeViewController *)
controller didFinishWithResult:(MessageComposeResult)result{
[controller dismissModalViewControllerAnimated:YES];} – (void)alertView:(UIAlertView *)
alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ if ([[alertView title]
isEqualToString:smsAlertViewTitle] && buttonIndex == 1) {
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"sms://+"]]; }}

Now looking Locating and Timing on background. Will post soon.

22 JuniOS 4.0 released, what’s that?

It’s a very expected update.

There are so much new features and possibilities that I couldn’t decide what to taste first and decided to continue everyday work. Later watching forums comments I see that the first thing everyone wants to do is to update an app by supporting 4.0, but surely it have to run on previous version also, 3.0-3.1. The right way is to open project settings and set:

  • Base SDK – iPhone Device 4.0
  • iPhone OS Deployment Target – iPhone OS 3.0

After recompiling it’ll turn to background properly, but still work on 3.0+ versions.

The second thing is the multitasking, of course.

There are two new UIApplicationDelegate’s methods for that. One when application was turned in to the background by pressing Home Button and one when turned out by pressing an icon on the spring board. As an example of background in/out workaround I provide using of Google Analytics, which is actually needed to be stopped and started:

- (void)applicationDidEnterBackground:(UIApplication *)application{
[[GANTracker sharedTracker] stopTracker];} -
(void)applicationWillEnterForeground:(UIApplication *)application{
[[GANTracker sharedTracker] startTrackerWithAccountID:@”xx-xxxxxxxxxxx ;)
dispatchPeriod:20             delegate:nil]; [[GANTracker sharedTracker]
trackPageview:@”/” withError:nil];

// you could animate splash screen there}

If you’ve not read about what background is for then try this and this. It was discussed earlier the release.
Apple developer documentation for background execution.

The last I couldn’t get if it’s necessary to show splash screen when an app is coming back to foreground. It’s blinking for a moment each time :( Since having an animated Default.png I just copy animating piece of code to “applicationWillEnterForeground” and it’s become better. But it’s not a common solution. See what Apple says about the Default.png in HIG (updated 2010-06-03):

To enhance the user’s experience at application launch, you must provide at least one launch image. A launch image looks very similar to the first screen your application displays. iPhone OS displays this image instantly when the user starts your application. As soon as it’s ready for use, your application displays its first screen, replacing the launch placeholder image.

Supply a launch image to improve user experience; avoid using it as an opportunity to provide:

An “application entry experience,” such as a splash screen 

An About window 

Branding elements, unless they are a static part of your application’s first screen

Because users are likely to switch among applications frequently, you should make every effort to cut launch time to a minimum, and you should design a launch image that downplays the experience rather than drawing attention to it.

Design a launch image that is identical to the first screen of the application, except for: 

Text. The launch image is static, so any text you display in it will not be localized.

UI elements that might change. Avoid including elements that might look different when the application finishes launching, so that users don’t experience a flash between the launch image and the first application screen.

Obviously there is no suggested solution yet. The problem is about foregrounding app may appear from any view, it’s not first main view as it’s used to be. So it seems better to use some branded image as a splash screen instead part of a UI. Or don’t use splash at all. It could also be a solution if we can set to ignore a splash when app is launched from background. Anyway not a big deal.

Other 

The other things I mentioned at this time is the iOS simulator was updated. Now it has all three devices: iPad, iPhone, iPhone 4. The last has a camera simulator, but it does not work as you could suggest, it’s just a gap. iPhone simulators have “iAd tester” and “Game Center” for a sandbox. Pretty cool.

One more option is TV Out. It’ll be useful for our great apps presentations. It probably could help to record app demos with a better quality.

Have a nice learning.

Remember Apple provides free videos from WWDC 2010 this year. I’m sure everybody will find useful stuff for your self.

UPD:
I think we should remember a timedate of ‘applicationDidEnterBackground’ for better analytics tracking. And when ‘applicationWillEnterForeground’ try to decide if it’s clear run or run from background. I use 120 seconds time difference and two url names for tracking like that:

- (void) startGoogleAnalytics{ [[GANTracker sharedTracker]
 startTrackerWithAccountID:@"XX-xxxxxxx-xx"             
dispatchPeriod:20             delegate:nil];  if (lastBackground)
{  if ([[NSDate date] timeIntervalSinceDate:lastBackground] > 120)
[[GANTracker sharedTracker] trackPageview:@"/" withError:nil];
else   [[GANTracker sharedTracker] trackPageview:@"/foreground" withError:nil];
[lastBackground release], lastBackground = nil; } else  
[[GANTracker sharedTracker] trackPageview:@"/" withError:nil];}

28 MarAnalog Clock using Quartz Core

Just want to share a simple example of using Quartz. I took a classic graphic task for – Analog Clock.

The drawing code is short and simple. It’s just necessary to undesrtand how measure angles of clock arrows from NSDatae and feel the difference between degrees and radians ;)

Inherite UIView and redefine “- (void)drawRect:(CGRect)rect ” method, which run when a UIView need to be displayed.
Mesure angles in readians from [NSDatae date] for Hour, Minute and Second.
Draw lines of clock arrows. A start point is the clock’s center. The end point is depends on measured angle.

inline double rad(double deg)
{
return deg / 180.0 * M_PI;
}

- (void) drawLineForContext:(const CGContextRef&)context Width:(float)_width angle:(double)_angle length:(double)radius
{
CGPoint c = CGPointMake(self.frame.size.width/2.0, self.frame.size.height/2.0);

CGContextSetLineWidth(context, _width);
CGContextMoveToPoint(context, self.center.x, self.center.y);
CGPoint addLines[] =
{
CGPointMake(self.frame.size.width/2.0, self.frame.size.height/2.0),
CGPointMake(radius*cos(_angle) +c.x, radius*sin(_angle) +c.y),
};

CGContextAddLines(context, addLines, sizeof(addLines)/sizeof(addLines[0]));
CGContextStrokePath(context);
}

- (void)drawRect:(CGRect)rect
{
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate* now = [NSDate date];
int h = [[cal components:NSHourCalendarUnit fromDate:now] hour];
int m = [[cal components:NSMinuteCalendarUnit fromDate:now] minute];
int s = [[cal components:NSSecondCalendarUnit fromDate:now] second];
[cal release];

BOOL isAM = hdouble hAlpha = rad((isAM?h:h-12)*30 +(30*m/60) -90);
double mAlpha = rad(m*6 -90);
double sAlpha = rad(s*6 -90);

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
[self drawLineForContext:context Width:8.0 angle:hAlpha length:self.frame.size.width/2.0 - 18];
[self drawLineForContext:context Width:5.0 angle:mAlpha length:self.frame.size.width/2.0 - 12];
[self drawLineForContext:context Width:2.0 angle:sAlpha length:self.frame.size.width/2.0 - 10];
}
To finish the AnalogClock class it’s need start and stop methods:
-(void)update
{
[self setNeedsDisplay];
}

-(void)startClockUpdates
{
[self update];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(update) userInfo:nil repeats:YES];
}
-(void)stopClockUpdates
{
[timer invalidate], timer = nil;
}

To use the clocks you need to add our custom UIView to any UIViewController and call the start method.

#import “AnalogClockViewController.h”

@implementation AnalogClockViewController

- (void) viewWillAppear:(BOOL)animated
{
[clock startClockUpdates];
}

- (void) viewWillDisappear:(BOOL)animated
{
[clock stopClockUpdates];
}

- (void)dealloc {
[super dealloc];
}

@end

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;}