Scrach Off Effect (Erasing layer)

Problem: I had to develop a demo app for a lottery ticket. It had to have a scratch off part, where user will scratch off a top layer to see if he had won anything.

Description: No description for you 🙂 Problem is quite obvious.

Solution:

I’ve created a view (I’ve called it ScratchView) that has a UIImageView on it. UIImageView is stretched across the whole view, but if you wanna you can pick your own width and height. Background of my view is set to clear color and I’ve painted my UIImageView with black color. Here’s the code that does it:

- (id)initWithFrame:(CGRect)frame

{

self = [super initWithFrame:frame];

if (self)

{

self.backgroundColor = [UIColor clearColor];

scratchField = [[UIImageView alloc] initWithFrame:self.bounds];

scratchField.backgroundColor = [UIColor clearColor];

[self addSubview:scratchField];

//overlay scratchField with black layer (rectagle)

[scratchField.image drawInRect:scratchField.bounds];

CGSize size;

size.height = scratchField.bounds.size.height;

size.width = scratchField.bounds.size.width;

UIGraphicsBeginImageContext(size);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetRGBFillColor(context, 0.96, 0.35, 0.0, 1.0);

CGContextFillRect(context, scratchField.bounds);

scratchField.image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

}

return self;

}

Just to point out, self, in this context is a view (ScratchView). It’s not a view controller, just a plain view.

Next thing I had to do is create a scratch of effect. It wasn’t that hard after I discovered one life saving line of code 🙂 But lets start from beginning. To create a scratch off effect i had to delete color (set color to clear) on my UIImageView. So what I did is I used touchesBegan and touchesMoved actions. On touchesBegan I just took start point of users touch. And in touchesMoved I’m erasing a line (or a curve if you wish) from users start point to current point. At the end I copy current point to users start point and if user continues to move just repeat the same method. Think that code is self explanatory but I wanna point out the money winning line of code. It’s this one:


CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);

You can set blending mode to clear and what that does is exactly what I was looking for. It doesn’t paint on your UIImageView, it sets it to clear. So where ever you move your finger it will set that path to clear. Here’s the rest of the code.


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

UITouch *touch = [touches anyObject];

if ([touch tapCount] == 2)

{

//taped 2 time

return;

}

userPoint = [touch locationInView:scratchField];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

UITouch *touch = [touches anyObject];

CGPoint currentPoint = [touch locationInView:scratchField];

UIGraphicsBeginImageContext(scratchField.frame.size);

[scratchField.image drawInRect:scratchField.bounds];

CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);

CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound

CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 25.0); // for size

CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0); //values for R, G, B, and Alpha

CGContextBeginPath(UIGraphicsGetCurrentContext());

CGContextMoveToPoint(UIGraphicsGetCurrentContext(), userPoint.x, userPoint.y);

CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);

CGContextStrokePath(UIGraphicsGetCurrentContext());

scratchField.image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

userPoint = currentPoint;

}

Hope this helps. You can find a full project here (btw: there is a view controller called ScratchTiceketViewController. Just ignore it. It was my test view controller and I’m to lazy to remove it :))

http://www.4shared.com/file/j1g6ieI5/TestingScratchTicketEffekt.html

All comments are welcomed. I publish all my blog notifications on my twitter so if you wanna you can follow me @ http://twitter.com/PavlovicMario

Posted in iOS Development, Objective-c, xcode | Tagged , , , , , , , , | 12 Comments

Get local time from iPhone [local NSDate]

Problem: [NSDate date] returns current time in GMT time zone. The question is how to get local time on users iPhone?

Description:

The question is quite straight forward so description will be short. When i encountered this problem I was amazed how hard it was to find an answer on the net. But fortunately I found a way to first get the local (system) time zone and then convert it to NSDate. Before I proved code that solves this problem I would like to point out that using GMT time zone is a common practice so if you’re storing your data in different time zone you have to be aware that it might cause incompatibility with 3rd party software. Just keep that in mind.

Solution:

NSDate* sourceDate = [NSDate date];

NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];

NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;

NSDate* destinationDate = [[[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate] autorelease];

First thing we do is get the GMT time zone and our system time zone. After that offset for each time zone is calculated. Difference in offset is used to create new date with time interval. Basically what happens is your [NSDate date] is “shifted” to correspond to local system time zone.

Hope this helps. All comments are welcomed. I publish all my blog notifications on my twitter so if you wanna you can follow me @ http://twitter.com/PavlovicMario

Posted in iOS Development, Objective-c, Uncategorized | Tagged , , , , , , , , , , | 12 Comments

Adding core data to project without autogenerate

Problem: Implementing core data to your project after you created the project without “use Core Data for storage”

Description: I’m new to iOS development and therefor I’m new to core data. So, I’ve created my project without the option “use Core Data for storage”. After few days of development I realised that NSUserDefaults just ain’t gonna cut it. I needed a database.

My first step was to Google “ios core data example” and get right to it. But soon I got bitch slapped by compiler and after that debugger took a swing too. My next move was a wee bit smarter 🙂 I took a step back and that leads us to our next chapter…

Solution:

I’ve decided to create a new project with “use Core Data for storage” options. I followed a step by step tutorial: http://developer.apple.com/library/ios/#DOCUMENTATION/DataManagement/Conceptual/iPhoneCoreData01/Introduction/Introduction.html

Tutorial is really nice and explains things in detail so I will not do the same thing. After implementing project explained in tutorial i ran my program and everything worked just fine. I’ve copy pasted stuff from my project with “use Core Data for storage” options to my old project and things just where not working again. I check all hard-coded strings twice and everything look OK.

At this point I was really stuck so I asked a help from experienced iOS developer. BTW: Tnx Chupa for bestowing me with a new-found knowledge 🙂

So the deal breaker was in managedObjectModel:

- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel_ != nil) {
        return managedObjectModel_;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"PrototypeMoneyMaker" withExtension:@"momd"];
    managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return managedObjectModel_;
}

When you create a project with “use Core Data for storage” option xcode generates model with momd extension. But when you create it yourself extension is mom. I’m not really sure why is that. So to keep things short. Your method should look like this:

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"PrototypeMoneyMaker" withExtension:@"mom"];

After I changed that everything worked fine. If it still doesn’t work for you try to check all hard-coded strings and in general compare your project that was created with “use Core Data for storage”.

Hope this helps. If anyone can explain this mysterious mom & momd extension I would be a happy little camper. All comments are welcomed. I publish all my blog notifications on my twitter so if you wanna you can follow me @ http://twitter.com/PavlovicMario

Posted in iOS Development, xcode | Tagged , , , , , , , | 1 Comment

Managing reference to objects in NSUserDefaults and Arrays trough strings

Quick one guys. I’m trying to force my self to write one blog each day, but.. Today is champions league and I’m tired and … yeah 🙂 So this is gonna be a short one.

Problem: Managing reference to objects in NSUserDefaults and Arrays trough strings.

Description: I was delighted to learn how easy it is to store data in iOS. What I didn’t like was hard-coded strings. Here’s an example how to set variable money in a NSUserDefaults under key “allMoney”:

- (void)resetAllMoney
{
NSNumber* money = [NSNumber numberWithDouble:0];
NSUserDefaults* repository = [NSUserDefaults standardUserDefaults];

[repository setObject:money forKey:@"allMoney"];
[repository synchronize];

[[NSNotificationCenter defaultCenter] postNotificationName:@"msgMoneySaved" object:nil];
}

So here’s my nifty solution:

I created a .h file that is basically empty and in it I define all my strings. I just include .h file in classes that use NSUserDetails and Arrays. Here’s an example of my .h file:

#import

#define myNsDefaultsPayCheckAmount		@"initParamPayCheck"
#define myNsDefaultsWorkHoursAmount		@"initParamWorkHours"
#define myNsDefaultsPayCheckType		@"initParamPayCheckType"
#define myNsDefaultsWorkHoursType		@"initParamWorkHourType"
#define myNsDefaultsCurrencyType		@"initParamCurrencyType"
#define myNsDefaultsAllMoney			@"allMoney"

@interface MyStrings : NSObject {}
@end

And now when we include MyString.h in any class we can easily use it like:

[repository setObject:money forKey:myNsDefaultsAllMoney];

One other kind of interesting thing happened while I was refactoring my code. I noticed that I had to change a lot of classes just to adjust to new “string managing” system. I didn’t smell right so I decided to crate a light weight data layer. After that air was nice and fresh, day was brighter and I could swear birds sang better 🙂

Posted in iOS Development | Tagged , , , , , , , , , | 1 Comment

Hide numpad in iOS [iPhone App Development]

Problem: How to hide numpad (keyboard with number) in my iPhone app

Description:

I’m building an iPhone app that has a view with two text fields in it. It’s expected from user to input some numbers in those fields so when user taps a field my app presents him a numpad. It’s easy to hide a standard keyboard because it has a return button. On a return button click you can just call a resignFirstResponder method on sender. But numpad doesn’t posses a return key.

Solution:

I’ve decided to hide a numpad when user taps out off numpad. At least that’s what I do when I want something stupid to retract. Just click away and hope for the best. So how did I accomplish this.

In my loadView method I’ve added a gesture recognizer to my parent view, like this:


UITapGestureRecognizer *viewBackgroundTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTap:)];

viewBackgroundTap.cancelsTouchesInView = NO;

[self.view addGestureRecognizer:viewBackgroundTap];

[viewBackgroundTap release];

My backgroundTap method looks like this:


- (IBAction)backgroundTap:(id)sender

{

[txtNumWorkingHours resignFirstResponder];

[txtPayCheckAmonut resignFirstResponder];

}

I would like to point one just one quick thing. By setting up cancelsTouchesInView to NO we allow touch to be passed to all children of a view. Without this line of code your view would hog a touch and would pass it to underlying buttons or other controls.

Hope this helps.

Arivederči 🙂

Posted in iOS Development | Tagged , , , , , | 2 Comments