Adding a reflection to an NSImage

To add an reflection in Cocoa to a NSImage object you can use the following NSImage category:

@interface NSImage(MKAddReflection)
- (NSImage*) addReflection:(CGFloat)percentage;
@end

@implementation NSImage(MKAddReflection)

- (NSImage*) addReflection:(CGFloat)percentage
{
	NSAssert(percentage > 0 && percentage <= 1.0, @"Please use percentage between 0 and 1");
	CGRect offscreenFrame = CGRectMake(0, 0, self.size.width, self.size.height*(1.0+percentage));
	NSBitmapImageRep * offscreen = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
														pixelsWide:offscreenFrame.size.width
														pixelsHigh:offscreenFrame.size.height
													 bitsPerSample:8
												   samplesPerPixel:4 
														  hasAlpha:YES
														  isPlanar:NO 
													colorSpaceName:NSDeviceRGBColorSpace
													  bitmapFormat:0
													   bytesPerRow:offscreenFrame.size.width * 4
													  bitsPerPixel:32];
	
	[NSGraphicsContext saveGraphicsState];
	[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreen]];
	
	[[NSColor clearColor] set];
	NSRectFill(offscreenFrame);
	
	NSGradient * fade = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.2] endingColor:[NSColor clearColor]];
	CGRect fadeFrame = CGRectMake(0, 0, self.size.width, offscreen.size.height - self.size.height);
	[fade drawInRect:fadeFrame angle:270.0];	
	
    NSAffineTransform* transform = [NSAffineTransform transform];
    [transform translateXBy:0.0 yBy:fadeFrame.size.height];
    [transform scaleXBy:1.0 yBy:-1.0];
    [transform concat];
	
	// Draw the image over the gradient -> becomes reflection
	[self drawAtPoint:NSMakePoint(0, 0) fromRect:CGRectMake(0, 0, self.size.width, self.size.height) operation:NSCompositeSourceIn fraction:1.0];
	
	[transform invert];
	[transform concat];

	// Draw the original image
	[self drawAtPoint:CGPointMake(0, offscreenFrame.size.height - self.size.height) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
	
	[NSGraphicsContext restoreGraphicsState];
	
	NSImage * imageWithReflection = [[NSImage alloc] initWithSize:offscreenFrame.size];
	[imageWithReflection addRepresentation:offscreen];
	
	return imageWithReflection;
}

To get a copy of a NSImage with a reflection applied you call [image addReflection:0.3], where the float value defines the percentage of the reflection regarding the height of the input image, e.g.

NSImage * input = [[NSImage alloc] initWithContentsOfFile:@"/Users/mk/Desktop/input.jpg"];
NSImage * output = [input addReflection:0.4];

Advertisements

Kerning comparison OSX – Windows – TLF

I did some tests comparing the same text on OS X with the TextEdit, on Windows XP with WordPad and on OS X in Safari on the Adobe Text Layout Framework demo site. I used everywhere Times New Roman as the font and a font size of 28.
Although Windows does not use kerning the text looks almost the same with some pixel difference. Have a look for yourself:
Comparing a text on OSX, Windows XP and TLF

Gesture recognition on the iPhone

Inspired by this detailed article by Carl D. Worth I began experimenting with stroke recognition on the iPhone. Unfortunately the sources for xstroke are very difficult to find nowadays and are unsupported. I finally did find them but I did not want to port all that X11 stuff, so I decided to start from scratch and did a small feasibility study which I want to show you here.

I created the project “KrikelKrakel” (German for scribbling) on BitBucket.

The most interesting class you would look at is KrikelKrakelView. It inherits from UIView and does all the tracking and recognition. The gestures are recognized when the touches ended. The area where touches took place is divided in a grid with 9 cells and the path the finger took is then described by the cell ids. You should have a look at the article mentioned earlier about the details.

One can register as a delegate to get called on different occassions:

- (void) willDrawGesture;

This will be called right inside the touchesBegan method.

- (void) didLearnNewGesture:(NSString*)text;

When a gesture has not been recognized the user will be presented with an alert box where he can enter a letter or some more text.

- (void) didRecognizeGesture:(NSString*)text;

When a gesture has been recognized this method will be called and the stored letter/text will be delivered in the variable text.

The learned gestures a stored in the application documents directory with the name “strokes.dict”. If there is no file on first start the bundled strokes.dict will be used a the initial version.

See a demo video here.

Howto retrieve the current language for Mac OS X

I have a client who needs localizations for several languages. To retrieve the current language I used

NSString * localeString = [[NSLocale currentLocale] localeIdentifier];
NSString * language = [[[localeString componentsSeparatedByString:@"_"] objectAtIndex:0] uppercaseString];

Most of the time the content of localeString represented the settings in the system preference pane. But if I set English as the first language and Finland as the region in Formats it returns “fi_FI” as the localeString, but uses the Finnish nib files.

Preference pane Language

Preference pange Formats

To get the languages in the order from the preference pane “Language” one has to use the following code:

NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:@"AppleLanguages"];
NSString* language = [[languages objectAtIndex:0] uppercaseString];

The variable language contains now “EN” as expected.

Using UITableViewCell with InterfaceBuilder

The code examples for the iPhone SDK show only how to construct your table cells programatically. If you want to use InterfaceBuilder to make a proper layout which might also be resizable etc. you can use this ViewFactory to handle the creation and reusage of your table cells.

First create a new empty Interface Builder file. Design all the UITableViewCells you want, but they must be root objects so they can be found by the ViewFactory. Each cell MUST have its “identifier” field set. It is in fact its reuseIdentifier required to make a proper recycling of your cell possible.

bild-1

Now create the ViewFactory, the header ViewFactory.h

@interface ViewFactory : NSObject {
    NSMutableDictionary * viewTemplateStore;
}

- (id) initWithNib: (NSString*)aNibName;

- (UITableViewCell*)cellOfKind: (NSString*)theCellKind forTable: (UITableView*)aTableView;

@end

and the implementation ViewFactory.m

#import "ViewFactory.h"

@implementation ViewFactory

- (id) initWithNib: (NSString*)aNibName
{
    if (self == [super init]) {
        viewTemplateStore = [[NSMutableDictionary alloc] init];
        NSArray * templates = [[NSBundle mainBundle] loadNibNamed:aNibName owner:self options:nil];
        for (id template in templates) {
            if ([template isKindOfClass:[UITableViewCell class]]) {
                UITableViewCell * cellTemplate = (UITableViewCell *)template;
                NSString * key = cellTemplate.reuseIdentifier;
                if (key) {
                    [viewTemplateStore setObject:[NSKeyedArchiver
                                                  archivedDataWithRootObject:template]
                                          forKey:key];
                } else {
                    @throw [NSException exceptionWithName:@"Unknown cell"
                                                   reason:@"Cell has no reuseIdentifier"
                                                 userInfo:nil];
                }
            }
        }
    }

    return self;
}

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

- (UITableViewCell*)cellOfKind: (NSString*)theCellKind forTable: (UITableView*)aTableView
{
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:theCellKind];

    if (!cell) {
        NSData * cellData = [viewTemplateStore objectForKey:theCellKind];
        if (cellData) {
            cell = [NSKeyedUnarchiver unarchiveObjectWithData:cellData];
        } else {
            NSLog(@"Don't know nothing about cell of kind %@", theCellKind);
        }
    }

    return cell;
}

@end

You have to make the ViewFactory instance available for your TableViews as a Singleton.

Whenever you want to fill you table cell in your UITableViewDataSource put this line in your method

- (UITableViewCell *)tableView: (UITableView *)aTableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
   UITableViewCell * cell = [viewFactory cellOfKind:@"news" forTable:aTableView];
   // Access the views using [cell viewWithTag:X] in the old way
   return cell;
}

where “news” in this example is the value of the reuseIdentifier.

Internally I store each instance of the UITableViewCells as a template in a NSData archived format and make it accessible via its reuseIdentifier. So whenever cellOfKind:forTable: is called it looks into the reuseQueue of the given table if there is already an old table cell which can be reused or if it needs to instantiate a new one using the archived template to create the new table view cell.

This technique is much more flexible if you are using tables and I hope you will have fun using it.

Quick iPhone Dev Tip: Creating an UIColor with just one RGB value

Are you also tired of splitting that long RGB value into red, green and blue and converting them to a format that UIColor understands? I was, so I wrote a macro that converts the value on compile time and returns an UIColor autorelease object with the correct values:

#define UIColorFromRGB(rgbValue) [UIColor \
 colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

In your code you can use it like this:

UIColor myGreen = UIColorFromRGB(0x00FF00);

Put this in your global header referenced from <project_name>_Prefix.pch and you can use it everywhere in your project without importing it excplicitly.

My first iPhone App has been released!

Yesterday evening I got a mail from Apple that they released my iPhone App WeFind. It took them 8 days (submitted on 30th September) to test it.

It’s a news aggregator which collects its data from our newssite Newsexpress. In the next version there will be other search engines included and many more features are already in the pipeline.

The direct AppStore link is here. Its only available in Germany, Switzerland and Austria since its based on German newspapers.

I am very excited.

News Aggregator

News Aggregator