
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!
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.
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
Panther introduced Key-Value Observing, a Cocoa implementation of the observer pattern. It’s very useful, but the API kind of sucks.
To get observation notices, you have to override a lengthy selector (
observeValueForKeyPath:ofObject:change:context:), provide a static context pointer, and essentially implement a big switch statement on the key path.That’s unwieldy, but I think it also makes for unmaintainable code: the callbacks end up thrown in the same method, and they’re separated from the observer registration.
KVO+Blocks is an
NSObjectcategory I wrote which providesaddObserverForKeyPath:task:, where the latter parameter is a block.So, before KVO+Blocks:
const static NSString *SomeValueObservationContext = @"org.andymatuschak.SomeValueObservationContext"; - (void)registerObservation { [observee addObserver:self forKeyPath:@"someValue" options:0 context:SomeValueObservationContext] } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == SomeValueObservationContext && [keyPath isEqualToString:@"someValue"]) { NSLog(@"someValue changed: %@", change); } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (void)dealloc { [observee removeObserver:self forKeyPath:@"someValue"]; [super dealloc]; }And after:
- (void)registerObservation { [observee addObserverForKeyPath:@"someValue" task:^(id obj, NSDictionary *change) { NSLog(@"someValue changed: %@", change); }]; }Note that you don’t even need to unregister your observer if you’re using GC; it’s handled for you when the observee is released. If you want to unregister early, or you’re using retain-counted code, there’s API for that too.
Blocks make life happy, ladies and gentlemen. More on this later.
Update: more notes and gotchas.
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.
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!
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]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.”);
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.