When writing Twitter clients, RSS reader or email apps - one always needs one thing. A relative date: -(NSString *)relativeDateStringFromDate:(NSDate *)date {
const int SECOND = 1;
const int MINUTE = 60 * SECOND;
const int HOUR = 60 * MINUTE;
const int DAY = 24 * HOUR;
const int MONTH = 30 * DAY;
NSDate *now = [NSDate date];
NSTimeInterval delta = [date timeIntervalSinceDate:now]* -1.0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSUInteger units = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit);
NSDateComponents *components = [calendar components:units fromDate:date toDate:now options:0];
NSString *relativeString;
if (delta < 0) {
relativeString = @"in the future";
} else if (delta < 1 * MINUTE) {
relativeString = (components.second == 1) ? @"one second ago" : [NSString stringWithFormat:@"%ld seconds ago",components.second];
} else if (delta < 2 * MINUTE) {
relativeString = @"a minute ago";
} else if (delta < 45 * MINUTE) {
relativeString = [NSString stringWithFormat:@"%ld minutes ago",components.minute];
} else if (delta < 90 * MINUTE) {
relativeString = @"an hour ago";
} else if (delta < 24 * HOUR) {
relativeString = [NSString stringWithFormat:@"%ld hours ago",components.hour];
} else if (delta < 48 * HOUR) {
relativeString = @"yesterday";
} else if (delta < 30 * DAY) {
relativeString = [NSString stringWithFormat:@"%ld days ago",components.day];
} else if (delta < 12 * MONTH) {
relativeString = (components.month
} else {
relativeString = (components.year
}
return relativeString;
}
Click here for the new weblog.
NSBundle-OBCodeSigningInfo
NSBundle category to see if an App is sandboxed and was downloaded from the Mac App Store. Very useful if you need to disable some features, that are simply not possible, when the App Store version is sandboxed…
Daily Snippet
Daily (or better monthly) Snippet
With the new Retina Display MacBook Pro and all the Macs with such an awesome display coming in the future - working with pixel based graphics is outdated. Drawing colors and gradients isn’t much of a problem - but also boring and sometimes a view needs a pattern or some noise.
Adding a subtle noise can be done very easily with a CIFilter:
CIImage *noisePattern = nil;
CIFilter *randomGenerator = [CIFilter filterWithName:@"CIColorMonochrome"];
[randomGenerator setValue: [[CIFilter filterWithName:@"CIRandomGenerator"] valueForKey:@"outputImage"] forKey:@"inputImage"];
[randomGenerator setDefaults];
noisePattern = [randomGenerator valueForKey:@"outputImage"];
[noisePattern drawAtPoint:NSZeroPoint fromRect:self.bounds operation:NSCompositeSourceOver fraction:0.04];
Daily Snippet
Garbage Collection is deprecated. ARC it is now and so we have to update/change our Mac projects. Sometimes this is very easy and, of course, often it is harder. And sometimes it is even impossible.
And sometimes there is a need to provide code for both. This can be done with:
#if !__has_feature(objc_arc)
// retain, release etc. here
#endif
Provide Retina Pictures via CSS
The new iPad has arrived and with it the need to provide Retina optimized images not only for Apps, but for websites as well:
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
background: transparent url(path_to/2x.png);
}
MGPreferencePanel Revised

I just revised my very often downloaded MGPreferencePanel code. I guess calling it the Cocoa Preference Window for the lazy ones made it that popular in the first place :–)
The MGPreferencePanel is now ready for Mountain Lion and ARC, so no more retained and autoreleased objects and I’ve also simplified the usage.
Drop the files into your project, provide titles and icons for views and that’s it. As always you can do whatever you want with it – Copyleft and WTFPL, you know.
About the MGPreferencePanel – The Cocoa Preference Window for the lazy ones
There is nothing more annoying than programming a preference-window with selectable toolbar-buttons and automatic resizing.
Most of the solutions you find today are either close to the one programmed by Matt Ball or base on it. Indeed, there are even more complicated ways, doing it.
They are all more or less understandable and customizable. But all have in common that it is annoying and time consuming to add each single subview via a separate nib-file or a separate plists. I know some disagree but I believe that more nibs aren‘t always the best solution. Because more nibs lead to more controller, that leads to more work customizing them, that leads to a more enraged programmer.
This is why I programmed my own solution.
Sure, it is far from being perfect, but you have customized everything in less than thirty two seconds!
You can use it as an inspector-view as well. Best of all: just one nib.
Anything left to be desired? Not for me.
Daily Snippet
Concurrency. And concurrency. And once again: concurrency. The holy grail to writing good Apps. And the hardest part. At least to debug.
The simplest way to performe a task not on the main thread is: [self performSelectorInBackground:@selector(myConcurrentAction) withObject:nil];
- (void) myConcurrentAction {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
// do whatever needs to be done
// but only all things NOT GUI
// ensure that nothing else tries to access used objects meanwhile
[pool drain];}
Daily Snippet
I needed a way to terminate an application from within another without using Apple Script or old Carbon stuff. I finally found this nifty solution: if ([[NSWorkspace sharedWorkspace] respondsToSelector:@selector(runningApplications)]) {
for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications])
if ([@"Suggestions" isEqualToString:[app localizedName]])
[app terminate];
}
Daily Snippet
NSValueTransfomers are a very handy solution to store a lots of information in a simple int for example. Combined with bindings one can display text in OS X label colors for example. Something I needed today:
@implementation MGIntToColorTransformer
+ (Class)transformedValueClass {
return [NSColor class];
}
+ (BOOL)allowsReverseTransformation {
return NO;
}
- (id)transformedValue:(id)value {
NSColor *color = nil;
switch ([value intValue]) {
case 0:
color = [NSColor colorWithCalibratedRed:0.298 green:0.298 blue:0.298 alpha:1.000]; // [NSColor blackColor] || [NSColor clearColor]
break;
case 1:
color = [NSColor colorWithCalibratedRed:0.924 green:0.372 blue:0.337 alpha:1.000]; // red
break;
case 2:
color = [NSColor colorWithCalibratedRed:0.907 green:0.628 blue:0.255 alpha:1.000]; // orange
break;
case 3:
color = [NSColor colorWithCalibratedRed:0.845 green:0.771 blue:0.254 alpha:1.000]; // yellow
break;
case 4:
color = [NSColor colorWithCalibratedRed:0.642 green:0.766 blue:0.254 alpha:1.000]; // green
break;
case 5:
color = [NSColor colorWithCalibratedRed:0.335 green:0.599 blue:0.937 alpha:1.000]; // blue
break;
case 6:
color = [NSColor colorWithCalibratedRed:0.712 green:0.522 blue:0.796 alpha:1.000]; // lavender
break;
case 7:
color = [NSColor colorWithCalibratedRed:0.624 green:0.624 blue:0.624 alpha:1.000]; // gray
break;
default:
color = [NSColor colorWithCalibratedRed:0.298 green:0.298 blue:0.298 alpha:1.000];
break;
}
return color;
}
@end