Quickly switch supported UIInterfaceOrientation for your View Controllers

In Xcode 4, you can simply point-and-click to specify supported orientation in your info.plist. But wait, it doesn’t really make your app obey to that certain interface orientation.

You still have to implement the - [UIViewController shouldAutorotateToInterfaceOrientation:] and return a right value.

Your specified value is located in your app’s info.plist, so here’s the code to let you easily access that configuration.

Example usage

// In your ViewController.m subclass that you wish to just obey the specified supported orientations,
// #import "UIApplicationAddition.h"
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsSupportedOrientation(interfaceOrientation);
}

Now you can just point-and-click to really change the supported orientations!

Adding Block support for UIControl’s Target-Action mechanism

Tired of Target-Action? Welcome to the world of blocks.

Sample usage:

UIButton *button = ...;
[button addEventHandler:^(id sender, UIEvent *event) {
    NSLog(@"touchedUp!");
} forControlEvent:UIControlEventTouchUpInside];

This is a very simple implementation of adding block support for UIControls. Download the snippet from here, instantly saves you a few lines of code.

If you are finding for a more complex block addition of everything, you maybe interested to have a look at zwaldowski / BlocksKit.

Splitting an array to several components

Say you’d like to split an array into several arrays each with a specific number of components.

// Say your originalArray is [@"A", @"B", @"C", @"D"]
NSArray *originalArray = [NSArray arrayWithObjects:@"A", @"B", @"C", @"D", nil];

// And we now want the array to be split into two arrays with two per segment
// like this [[@"A", @"B"], [@"C", @"D"]]
NSArray *newArray = [NSArray splitArray:originalArray componentsPerSegment:1];


And here’s the code snippet on how you can do it.

//
//  NSArray+JTArraySplit.h
//
//  http://ioscodesnippet.tumblr.com
//

#import <Foundation/Foundation.h>

@interface NSArray (JTArraySplit)

+ (NSArray *)splitArray:(NSArray *)targetArray componentsPerSegment:(NSUInteger)componentsCount;

@end


//
//  NSArray+JTArraySplit.m
//
//  http://ioscodesnippet.tumblr.com
//

#import "NSArray+JTArraySplit.h"

@implementation NSArray (JTArraySplit)

+ (NSArray *)splitArray:(NSArray *)targetArray componentsPerSegment:(NSUInteger)componentsCount {
    NSMutableArray *splitedArray = [NSMutableArray array];

    NSUInteger targetArrayCount = [targetArray count];

    if (targetArrayCount > 0) {
        int index = 0;
        while (index < targetArrayCount) {
            int length = MIN(targetArrayCount - index, componentsCount);
            NSArray *subArray = [targetArray subarrayWithRange:NSMakeRange(index, length)];
            [splitedArray addObject:subArray];
            index = index+length;
        }
        return splitedArray;
    } else {
        // no objects inside targetArray, so just return empty array
        return splitedArray;
    }
}

@end
Force decompressing UIImage in background to achieve better performance

If you’ve ever experience in loading lots of image in your app from the web, and display in a list form of UIImages in a table view, you’d properly heard of doing lazy loading those images. There are several great loading and caching open source solutions you’d probably already heard of such as SDWebImage, EGOImageLoading, etc.

However, you are still experience slight UI delay when the image finished loading or caching out from the disk. The reason behind is UIKit does extra lazy initialization, and only do expensive decompressing at the time to display or draw.

Here’s is code snippet meant to be load from a background thread that force an image to be decompressed into the right format, so that the system don’t have to do extra conversion on display.

//
//  UIImage+JTImageDecode.h
//
//  Created by james on 9/28/11.
//  http://ioscodesnippet.tumblr.com
//

@interface UIImage (JTImageDecode)
+ (UIImage *)decodedImageWithImage:(UIImage *)image;
@end



//
//  UIImage+JTImageDecode.m
//
//  Created by james on 9/28/11.
//  http://ioscodesnippet.tumblr.com
//

@implementation UIImage (JTImageDecode)

+ (UIImage *)decodedImageWithImage:(UIImage *)image {
    CGImageRef imageRef = image.CGImage;
    // System only supports RGB, set explicitly and prevent context error
    // if the downloaded image is not the supported format
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 CGImageGetWidth(imageRef),
                                                 CGImageGetHeight(imageRef),
                                                 8,
                                                 // width * 4 will be enough because are in ARGB format, don't read from the image
                                                 CGImageGetWidth(imageRef) * 4,
                                                 colorSpace,
                                                 // kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little 
                                                 // makes system don't need to do extra conversion when displayed.
                                                 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); 
    CGColorSpaceRelease(colorSpace);

    if ( ! context) {
        return nil;
    }
    
    CGRect rect = (CGRect){CGPointZero, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)};
    CGContextDrawImage(context, rect, imageRef);
    CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    
    UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef];
    CGImageRelease(decompressedImageRef);
    return [decompressedImage autorelease];
}

@end 

So after an image has successfully loaded from the web or cached out, create an operation and decompress the image with 

[UIImage decodedImageWithImage:anImage];

And now you can achieve a smoother scrolling experience for your app.

You can also download UIImage+JTImageDecode directly on gist or checkout the optimized mystcolor/SDWebImage library on github.

Adding drop shadow on UINavigationBar

Somehow adding drop shadows on UINavigationBar using the CALayer property fails for me but I later find it out we just need a little trick there.

// The magic is to have -[UIView clipToBounds] set to NO
self.navigationController.navigationBar.clipsToBounds = NO;



Base on the fact this is ioscodesnippet, I know I have to make a really simple UINavigationBar+JTDropShadow category to make our life more easier.

//
//  UINavigationBar+JTDropShadow.h
//
//  Created by james on 9/20/11.
//  http://ioscodesnippet.tumblr.com
//


#import 

@interface UINavigationBar (JTDropShadow)

- (void)dropShadowWithOffset:(CGSize)offset
                      radius:(CGFloat)radius
                       color:(UIColor *)color
                     opacity:(CGFloat)opacity;


@end




//
//  UINavigationBar+JTDropShadow.m
//
//  Created by james on 9/20/11.
//  http://ioscodesnippet.tumblr.com
//


#import "UINavigationBar+JTDropShadow.h"

#import 



@implementation UINavigationBar (JTDropShadow)

- (void)dropShadowWithOffset:(CGSize)offset
                      radius:(CGFloat)radius
                       color:(UIColor *)color 
                     opacity:(CGFloat)opacity {

    // Creating shadow path for better performance
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, self.bounds);
    self.layer.shadowPath = path;
    CGPathCloseSubpath(path);
    CGPathRelease(path);

    self.layer.shadowColor = color.CGColor;
    self.layer.shadowOffset = offset;
    self.layer.shadowRadius = radius;
    self.layer.shadowOpacity = opacity;

    // Default clipsToBounds is YES, will clip off the shadow, so we disable it.
    self.clipsToBounds = NO;

}

@end



Usage:


- (void)viewDidLoad {
    [super viewDidLoad];
    ...
    [self.navigationController.navigationBar dropShadowWithOffset:CGSizeMake(0, 3)
                                                           radius:1
                                                            color:[UIColor darkGrayColor]
                                                          opacity:1];
    ...
}



Don’t forget to import QuartzCore.framework in your build settings.

While it is more generic by making it a UIView category, but I’ll leave it simple here to demonstrate the main purpose.

How about a screenshot this time? ;P

Creating an image of image in specific rect or by proportion
CGImageCreateWithImageInRect

That’s already defined in the Foundation framework. It is absolute fine for who already familiar well enough with CoreGraphics and doesn’t mind manually take care of the image orientation.

We loved UIKit anyway.

Use this UIImage+JTImageCrop category instead.

//
//  UIImage+JTImageCrop.h
//
//  Created by james on 9/8/11.
//  http://ioscodesnippet.tumblr.com
//

#import <UIKit/UIKit.h>


@interface UIImage (JTImageCrop)

+ (UIImage *)imageWithImage:(UIImage *)image cropInRect:(CGRect)rect;

// define rect in proportional to the target image.   
//
//  +--+--+
//  |A | B|
//  +--+--+
//  |C | D|
//  +--+--+
//
//  rect {0, 0, 1, 1} produce full image without cropping.
//  rect {0.5, 0.5, 0.5, 0.5} produce part D, etc.

+ (UIImage *)imageWithImage:(UIImage *)image cropInRelativeRect:(CGRect)rect;

@end

// Used by +[UIImage imageWithImage:cropInRelativeRect]
CGRect CGRectTransformToRect(CGRect fromRect, CGRect toRect);

//
//  UIImage+JTImageCrop.m
//
//  Created by james on 9/8/11.
//  http://ioscodesnippet.tumblr.com
//

#import "UIImage+JTImageCrop.h"


CGRect CGRectTransformToRect(CGRect fromRect, CGRect toRect) {
    CGPoint actualOrigin = (CGPoint){fromRect.origin.x * CGRectGetWidth(toRect), fromRect.origin.y * CGRectGetHeight(toRect)};
    CGSize  actualSize   = (CGSize){fromRect.size.width * CGRectGetWidth(toRect), fromRect.size.height * CGRectGetHeight(toRect)};
    return (CGRect){actualOrigin, actualSize};
}

@implementation UIImage (JTImageCrop)

+ (UIImage *)imageWithImage:(UIImage *)image cropInRect:(CGRect)rect {
    NSParameterAssert(image != nil);
    if (CGPointEqualToPoint(CGPointZero, rect.origin) && CGSizeEqualToSize(rect.size, image.size)) {
        return image;
    }

    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1);
    [image drawAtPoint:(CGPoint){-rect.origin.x, -rect.origin.y}];
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return croppedImage;
}

+ (UIImage *)imageWithImage:(UIImage *)image cropInRelativeRect:(CGRect)rect {
    NSParameterAssert(image != nil);
    if (CGRectEqualToRect(rect, CGRectMake(0, 0, 1, 1))) {
        return image;
    }
    
    CGRect imageRect = (CGRect){CGPointZero, image.size};
    CGRect actualRect = CGRectTransformToRect(rect, imageRect);
    return [UIImage imageWithImage:image cropInRect:CGRectIntegral(actualRect)];
}

Now you use this instead of the CoreGraphics method.

+ (UIImage *)imageWithImage:(UIImage *)image cropInRect:(CGRect)rect;

Last but not least, you’d somehow want to further abstract it with a proportional rect value. (Imagine you are defining a cropping area on the screen and want to crop a full sized image, you’d transform the visible area to the full sized photo).

A handly method is also created for this purpose.

+ (UIImage *)imageWithImage:(UIImage *)image cropInRelativeRect:(CGRect)rect;


While you may be interested in more of it, go visit JTImageKit on GitHub! Documentations are raw right now, but welcome any comments!

Adding fadeout effect to any -[UIViews removeFromSuperview]

Typically you like to do something like below when you wanted to remove a view from its parent view.

[myView removeFromSuperview];


Sometimes it’s not that please for a user to see an UI component disappearing suddenly. You’d then consider adding some transition effects, and here’s a little code snippet in the UIView+JTRemoveAnimated category for how you can get a fade out effect on view removal.

//
//  UIView+JTRemoveAnimated.h
//
//  Created by james on 9/1/11.
//  http://ioscodesnippet.tumblr.com/
//
@interface UIView (JTRemoveAnimated)

- (void)removeFromSuperviewAnimated;

@end


//
//  UIView+JTRemoveAnimated.m
//
//  Created by james on 9/1/11.
//  http://ioscodesnippet.tumblr.com/
//
#import <QuartzCore/QuartzCore.h>

@implementation UIView (JTRemoveAnimated)

// remove static analyser warnings
#ifndef __clang_analyzer__

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
    if ([animationID isEqualToString:@"fadeout"]) {
        // Restore the opacity
        CGFloat originalOpacity = [(NSNumber *)context floatValue];
        self.layer.opacity = originalOpacity;
        [self removeFromSuperview];
        [(NSNumber *)context release];
    }
}

- (void)removeFromSuperviewAnimated {
    [UIView beginAnimations:@"fadeout" context:[[NSNumber numberWithFloat:self.layer.opacity] retain]];
    [UIView setAnimationDuration:0.3];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
    [UIView setAnimationDelegate:self];
    self.layer.opacity = 0;
    [UIView commitAnimations];
}

#endif

@end


So from now on, you just needed to do this to fade out any UIViews

[myView removeFromSuperviewAnimated]
NSStringf. Simpler printf styled +[NSString stringWithFormat:]

If you thinks that +[NSString stringWithFormat:] is simply annoying.

If you missed the C style string formatter like printf() or NSLog().

[NSString stringWithFormat:@”Why should I type this long?”];

//
//  JTStringAddition.h
//
//  Created by James Tang on 27/08/2011.
//  http://ioscodesnippet.tumblr.com/
//

NSString *NSStringf(NSString *format, ...);


//
//  JTStringAddition.m
//
//  Created by James Tang on 27/08/2011.
//  http://ioscodesnippet.tumblr.com/
//

#import "JTStringAddition.h"

NSString *NSStringf(NSString *format, ...) {
    va_list ap;
    NSString *str;
    va_start(ap,format);
    str=[[NSString alloc] initWithFormat:format arguments:ap];
    va_end(ap);

    return [str autorelease];
}


NSStringf(@”It’s just so much easier. %@”, @”Really.”);

Rendering any UIViews into UIImage in one line

Looks like you’d like to make some snapshots of your application, or maybe capturing partial UI elements on the screen for caching or saving? You can achieve this in just one single line like this.

UIImage *viewSnapshot = [myView toImage];


Add this UIView+JTViewToImage category to your project, and you’ll also needed to link <QuartzCore/QuartzCore.h> framework too.

//
//  UIView+JTViewToImage.h
//
//  Created by James Tang on 25/08/2011.
//  http://ioscodesnippet.tumblr.com/
//

#import <UIKit/UIKit.h>

@interface UIView (JTViewToImage)

// - [UIImage toImage]
//
// Follow device screen scaling. If your view is sized 320 * 480, 
// it renders 320 * 480 on non-retina display devices, 
// and 640 * 960 on retina display devices
// Use this option for making high resolution view elements snapshots to display on retina devices
- (UIImage *)toImage;

// - [UIImage toImageWithScale]
//
// Force rendering in a given scale. Commonly used will be "1".
// Good for output or saving a static image with the exact size of the view element.
- (UIImage *)toImageWithScale:(CGFloat)scale;

@end



//
//  UIView+JTViewToImage.m
//
//  Created by James Tang on 25/08/2011.
//  http://ioscodesnippet.tumblr.com/
//

#import "UIView+JTViewToImage.h"
#import <QuartzCore/QuartzCore.h>

@implementation UIView (JTViewToImage)

- (UIImage *)toImage {
    return [self toImageWithScale:0];
}

- (UIImage *)toImageWithScale:(CGFloat)scale {
    // If scale is 0, it'll follows the screen scale for creating the bounds
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, scale);
    
    // - [CALayer renderInContext:] also renders subviews 
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];

    // Get the image out of the context
    UIImage *copied = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Return the result
    return copied;
}

@end


In advance, if you want to make sure you’ve the exact size of the static image output, try this line instead.

UIImage *viewSnapshot = [myView toImageWithScale:1];


This will tell your app to ignore the screen scale and simply reference to the size of the view bounds.