Self-contained, two-file (.h/.m) iPhone/iPad components that are dead-easy to drop into your projects
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
.gitignore Added build folders to .gitignore Apr 8, 2011

Self-Contained Components for iOS

Two-file (.h / .m) useful components and utility classes that are dead-easy to drop into your iOS projects.

License: MIT (free for any use, no attribution required).

Follow us on twitter: @SoloComponents.

Note: I want to collect as many open-source components as possible, not just publish my own ones. If you have a useful iOS class that does not depend on anything, feel free to fork, add and send me a pull request!


ATPagingView is a wrapper around UIScrollView in (horizontal) paging mode, with an API similar to UITableView.

Status: production-ready, used by at least one App Store app.

You provide the page views by implementing two delegate methods:

- (NSInteger)numberOfPagesInPagingView:(ATPagingView *)pagingView {
    return 10;

- (UIView *)viewForPageInPagingView:(ATPagingView *)pagingView atIndex:(NSInteger)index {
    UIView *view = [pagingView dequeueReusablePage];
    if (view == nil) {
        view = [[[DemoPageView alloc] init] autorelease];
    return view;

You are also notified when the user navigates between pages:

- (void)currentPageDidChangeInPagingView:(ATPagingView *)pagingView {
    self.navigationItem.title = [NSString stringWithFormat:@"%d of %d", pagingView.currentPageIndex+1, pagingView.pageCount];

You can use ATPagingView directly or derive your view controller from ATPagingViewController.

ATPagingViewController is similar to UITableViewController and:

  • defines loadView to create ATPagingView automatically,
  • sets itself as a delegate of ATPagingView,
  • calls reloadData in viewWillAppear: if the paging view is empty,
  • additionally it forwards orientation events to the paging view (see below).

If you want to use ATPagingView without ATPagingViewController, you need to:

  • provide your delegate object using the delegate property,

  • call reloadData to populate the view,

  • if you want to support rotation, you need to invoke willAnimateRotation and didRotate methods from your view controller:

    - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
        [self.pagingView willAnimateRotation];
    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
        [self.pagingView didRotate];


A container that arranges its items in rows and columns similar to the thumbnails screen in, the API is modeled after UITableView.

Status: beta.

Enjoy the familiar delegate methods:

- (NSInteger)numberOfItemsInArrayView:(ATArrayView *)arrayView {
    return 97;

- (UIView *)viewForItemInArrayView:(ATArrayView *)arrayView atIndex:(NSInteger)index {
    DemoItemView *itemView = (DemoItemView *) [arrayView dequeueReusableItem];
    if (itemView == nil) {
        itemView = [[[DemoItemView alloc] init] autorelease];
    return itemView;

There's ATArrayViewController which further reduces the amount of boilerplate code you have to write. Similar to UITableViewController, it:

  • overrides loadView to create ATArrayView automatically,
  • sets itself as a delegate of the array view,
  • calls reloadData in viewWillAppear: if the array view is empty.


Allows to easily use an image (CGImageRef) backed by a malloc'ed chunk of memory. This means you can read or manipulate image bytes directly.

Status: ready for production use.

Using ATByteImage:

ATByteImage *blurred = [[ATByteImage alloc] initWithSize:blurredSize];
[blurred clear];

ATByteImageContext *blurredContext = [blurred newContext];
CGContextSetBlendMode(blurredContext.CGContext, kCGBlendModeNormal);
... draw using blurredContext.CGContext ...
[blurredContext release];

  UIImage *myOverlay = [blurred extractImage];

Here's another example. The following function is useful in background image loading code:

// Returns an uncompressed (decoded) UIImage, optimized for drawing speed.
// This is a middle ground between [UIImage imageNamed:] and a plain
// [UIImage imageWithContentsOfFile:], as follows:
// * [UIImage imageWithContentsOfFile:] loads image data from disk and
//   decodes it each time you display the image.
//   If you are using CATiledLayer to display a large image (and you should,
//   since UIImageView is not recommended for images bigger than ~1024x1024),
//   the whole JPEG will decoded for EACH tile you display.
// * [UIImage imageNamed:@"xxx"] only ever decodes the image once, just as you
//   wanted. However it also caches the image and seems to sometimes (always?)
//   not release the data even after you release your UIImage.
//   An app that loads several large images via 'imageNamed' will thus crash
//   quite soon with unfamous "error 0".
//   Another undesired quality of 'imageNamed' is that the image is loaded and
//   decoded when it is displayed for the first time, which means you can't
//   really do the decoding in a background thread.
// * DecompressUIImage([UIImage imageWithContentsOfFile:@"xx.jpg"]) is the
//   sweet spot between the two — it returns a fully decoded image which can
//   be displayed quickly, and memory management is entirely up to you.
UIImage *DecompressUIImage(UIImage *image) {
    ATByteImage *byteImage = [[[ATByteImage alloc] initWithImage:image] autorelease];
    return [byteImage extractImage];