May 032013
 

If you’ve been coding iOS for even a short period of time you’ve come across presentViewController. (If you are using a UINavigationViewController, you have pushViewController.) You are limited, however, in the animations you can use: cover vertical, flip horizontal, or cross dissolve.

I have a client who need left, right, up and down swipes. The app wasn’t a traditional drill-down then back back back out app. It was very free-form. So I needed to roll my own.

I call the class the EnkiUnorderedController. The Enki is from the name of my consulting company, Enki Labs, and the Unordered part refers to the random access nature of the UIViewControllers it displays. Lets look at the class interface first. (The source is all available in the open source EnkiUtils project, but the source there has comments in it!)

@interface EnkiUnorderedController : UIViewController
@property (strong, nonatomic)  UIViewController *current;
@property (strong, nonatomic)  UIViewController *cover;

- (void)initCurrentWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
- (void) replaceViewController:(UIViewController *) target withDirection:(slideDirection) direction;
- (void) coverViewController:(UIViewController *) target withDirection:(slideDirection) direction;

- (void) uncoverViewControllerwithDirection:(slideDirection) direction;
@end

To use an EnkiUnorderedViewController you create one and initialize with the UIViewController you want it to display.

self.unorderedController =  [[EnkiUnorderedController alloc] init];
[_unorderedController initCurrentWithNibName:@"MAWJump1ViewController" bundle:nil];

In this example, I have a UIViewController subclass named MAWJump1ViewController and its layout is in the xib file with the same name. Lets look at initCurrentWithNibName’s implementation:

- (void)initCurrentWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    Class vcClass = NSClassFromString (nibNameOrNil);
    self.current = [[vcClass alloc] initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
    if (self.current) {
        
        self.current.view.frame = self.view.frame;

        [self addChildViewController:self.current];
        [self.current didMoveToParentViewController:self];
        [self.view addSubview: self.current.view];
        
    }
}

Notice that it uses both reflection and container view controllers. Doing this pre-iOS5 would have been difficult!

The first thing I do is create an object based on the class name. (Yes, this means the class name must be the same as the xib file name.) I alloc one of these and call the expected initWithNibName on it. I store this in the property named current. (And right now, when writing this blog, I’m wondering if I should make current a class extension, so it is private.) I grab the frame of the containing class and then use the container view controller methods to install it as a child view controller.

So far this seems like an awful lot of work with no benefit. But, what happens when you want to replace the view controller with another one?

    MAWAppDelegate  * appDelegate = (MAWAppDelegate *)[[UIApplication sharedApplication] delegate];
    EnkiUnorderedController *unVC = appDelegate.unorderedController;
    
    [unVC replaceViewController:[self randomVC] withDirection:[self randomDirection]];

In my sample app I store the EnkiUnorderedViewController in the app delegate and simply make a call to replaceViewController to bring the new one in. (randomVC and randomDirection are methods in the sample app to give us a random view controller and a random direction, they aren’t really all that interesting, but see the sample code if you want to.) The direction is specified by an enum:

typedef enum {
    kSlideUndefined = 0,
    kSlideUp = 1,
    kSlideDown = 2,
    kSlideLeft = 3,
    kSlideRight = 4,
} slideDirection;

Lets look at replaceViewController’s implementation:

(slideDirection) direction;
{
    [self replaceViewController:target withDirection:direction withCover:NO];
}

- (void) coverViewController:(UIViewController *) target withDirection:(slideDirection) direction;
{
    [self replaceViewController:target withDirection:direction withCover:YES];
}

Ok, that isn’t too interesting. I’m just calling a private method that says if we are covering the view controller or not. (Covering is something the client’s app needed. When you replace a view controller the original one is gone, when you cover it, you can uncover it later and get it back, this is much like UINavigationController’s push and pop methods except that I have written the stack yet.) Lets look at the real code:

- (void) replaceViewController:(UIViewController *) target withDirection:(slideDirection)direction withCover:(BOOL) coverIt;
{
    target.view.frame = self.view.frame;
   
    // add the target as a child view controller.
    [self addChildViewController:target];
    [target didMoveToParentViewController:self];
    [self.view addSubview: target.view];

    // knock the target off the screen to start with
    [self adjustTarget:target withDirection:direction];
    
    [UIView animateWithDuration:0.5
                          delay:0
                        options: UIViewAnimationOptionCurveEaseInOut
                     animations:^{
                         // slide the current one away
                         [self slideCurrent:_current withDirection:direction];
                         
                         // slide the target in
                         [self slideTarget:target withDirection:direction];                         
                     }
                     completion:^(BOOL finished){
                         if (coverIt) {
                             if (_cover != nil) {
                                 [_cover removeFromParentViewController];
                             }
                             _cover = target;
                         }else {
                             [_current removeFromParentViewController];
                             _current = target;
                         }
                     }];
}

We grab the target (the new one) and add it to the EnkiUnorderedViewController. So now the parent has 2 contained view controllers. We call the internal method adjustTarget just to get it off the screen. We use the animateWithDuration block method to slide the current view controller away and then slide the new one in. Finally, using the completion block with either remove the current view controller and set the target to current, or if we are covering we set the cover to target, leaving the current intact.

Yes, there is rectangle math in there also, it isn’t very interesting but download the full sample project if you want to see it.

Feb 012013
 

Years ago I was learning Java on the job and my co-worker Scott mentioned reflection to me. I had never heard of it, it was wild stuff and I could see that someday it could really come in handy again. Well it has.

I had a UI I had to build for my iOS app. It was just a vertical menu that was implemented as group of UITableViewCells. I had built an array of them to populate the table, that was easy.

   self.actionArray = @[
        @"Unicorns",
        @"Ponies",
        @"Settings",
        @"Privacy Policy",
        @"About",
    ];

And I wrote the usual UITableViewDelegate code to populate the table from that array. But how could I create the view controllers? It was easy to add them to the array;

   self.actionArray = @[
        @[ @"Unicorns", @"UnicornsViewController"],
        @[ @"Ponies", @"PoniesViewController"],
        @[ @"Settings", @"SettingsViewController"],
        @[ @"Privacy Policy", @"PrivacyViewController"],
        @[ @"About", @"AboutViewController"],
    ];

Then I remember that bit that Scott taught me, reflection. I had the class name, surely I could do something with it. And I could:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    Class vcClass = NSClassFromString ([[actionArray_ objectAtIndex:[indexPath row]] objectAtIndex:1]);

    UIViewController *detailViewController = [[vcClass alloc] initWithNibName:[[actionArray_ objectAtIndex:[indexPath row]] objectAtIndex:1] bundle:nil];

    [self.navigationController pushViewController:detailViewController animated:YES];
}

When you tapped a cell the view controller’s name was extracted from the array. I got a class object for it, alloced one of those and inited with the nib of the same name.

Jan 192013
 

iOS5 added something a lot of iOS developers have been needing, container view controllers. This lets you put a view controller inside another view controller. This is wonderful for countless reasons, but the one that draws me is reuse and abstraction.

I was working on a app which had 2 similar windows, they had a top part (body) and a footer. The top part was easy, they each had their own UIViewControllers. But when I went to add the footer to the second one I got to thinking DRY, I was repeating myself and that’s never a good idea. So I abstracted out a parent class and I went to put the footer code in and I got stuck, I wanted my footers to also be view controllers.

I remembered a talk I attended about new iOS5 features and container view controllers was mentioned. After a bit of googling around I had it.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // set up the footer contained view, this has nothing to do with table footers, this is below
    // the tableview
    //
    FooterViewController *vc = [[FooterViewController alloc] initWithNibName:@"FooterViewController"  bundle:nil];
    vc.view.frame = self.footerFPOView.frame;
    vc.delegate = self;
    
    [self addChildViewController:vc];
    [vc didMoveToParentViewController:self];
    [self.view addSubview: vc.view];
}

Of course there are a few things to note. First, hooray, I’m loading my footer from a xib file. I’ve placed a UIVIew called footerFPOView in the outer view controller’s xib, this is a trick I use all the time. FPO stands for For Position Only and that lets me use interface builder for positioning. I communicate with a protocol, hence the delegate. And then I call addChildViewController to add it, and then I tell the new one that it has a new parent, and finally, add its view.

This is just a few lines of code yet the window should respond to both view controllers and respond to rotations.

Jan 142013
 

I was working on a cocos2d based tapping game and I wasn’t able to run my game twice. It was clear that I wasn’t shutting the 1st game down correctly or building the 2nd game correctly, or both!

There are a lot of tutorials on the web out there teaching you how to add UIKit buttons to your Cocos2D app, or how to launch Cocos2D from your UIKit based app. But I needed to do both. I wanted a UIViewController under my game and UIKit widgets on top of my game. I spent a lot of time reading and this is what I came up with.

First, building the Xcode project was a nightmare. I eventually used the cocos2d/box2d template and then ripped out the files I didn’t needed, and added all my original files back in. The AppDelegate.m file looks just like a non-cocos2d app should look. This goes against the grain of many of the tutorials which advise you to build your cocos2d environment in the AppDelegate. I struggled with that, didn’t have luck for most of a Friday and then on Monday I put in a Cocos2DSingleton and it pretty much ran first time.

Here is my GameViewController’s viewDidLoad method:

- (void)viewDidLoad
{
    [super viewDidLoad];

    srandom(time(0));
    
    [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:NO];
    
    TTCocos2DSingleton *shared = [TTCocos2DSingleton sharedCocos2D];
    CCGLView *glView = [shared currentGLView];

    [self.view insertSubview:glView atIndex:1];
}

There are a view things to note. GameViewController has game UIButtons, score UILabels, and other game type of UI widgets. This lets me do a lot of the game controls in Interface Builder, not laying them out by hand. I first initialize srandom just to seed the random number generator. Then I hide the status bar since the game is full-screen.

I get my cocos2d instance via the singleton, get its glView and insert this into the GameViewController’s view at index 1. This puts it below all the game controls. I’ll show you the sharedCocos2D method later, lets look at viewWillAppear.

- (void) viewWillAppear:(BOOL)animated
{
    if(![[CCDirector sharedDirector]  runningScene]){
        CCScene *scene = [MyGameLayer scene];
        
        myGame = [MyGameLayer node];
        myGame.delegate = self;
        
        [scene addChild: myGame];
        
        [[CCDirector sharedDirector] runWithScene:scene];
    } else {
        // we have a scene already, replace the original to get a new game
        [[CCDirector sharedDirector] startAnimation];
        
        CCScene *scene = [MyGameLayer scene];
        
        myGame = [MyGameLayer node];
        myGame.delegate = self;
        
        [scene addChild: myGame];
        
        [[CCDirector sharedDirector] replaceScene:scene];
    }
}

Notice how we treat the first run differently from the second run. For the second, and subsequent runs, we replace the scene with a new one. This avoids all “restarting” problems. Also notice that I set a delegate. I use a delegate protocol to communicate between my game layer and my UIViewController.

My singleton pattern comes from the Duck Rowing blog which I must admit is a pretty awesome name for a blog. I’m not going to show all the singleton code here, this blog is about cocos2d, but here is how I build my cocos2d environment.

+ (TTCocos2DSingleton *) sharedCocos2D;
{
    static dispatch_once_t onceQueue;
    
    dispatch_once(&onceQueue, ^{
        if (sharedInstance) {
            return;
        }
        sharedInstance = [[TTCocos2DSingleton alloc]init];
        // Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits
        sharedInstance->glView = [CCGLView viewWithFrame:[[UIScreen mainScreen] bounds]
                             pixelFormat:kEAGLColorFormatRGB565	//kEAGLColorFormatRGBA8
                             depthFormat:0	//GL_DEPTH_COMPONENT24_OES
                      preserveBackbuffer:NO
                              sharegroup:nil
                           multiSampling:NO
                         numberOfSamples:0];
        [sharedInstance->glView setMultipleTouchEnabled:YES];
        [sharedInstance setupDirector];
    });
    return sharedInstance;
    
}

The singleton sets up the CCGLView, enables multi-touch and then sets up the director. (I put that in another method since I thought, erroneously, that I’d need to call it elsewhere. Turns out I didn’t need to.)

- (void)setupDirector
{
    CCDirectorIOS *director = (CCDirectorIOS*) [CCDirector sharedDirector];
    
    [director setView:glView];
    [director enableRetinaDisplay:YES];
    director.wantsFullScreenLayout = YES;
    [director setDisplayStats:NO];
    [director setAnimationInterval:1.0/60];
}

And in setupDirector we set the usual suspects needed for a cocos2d app. Now the game can be played multiple times, I have a full UIViewController/UINavController underneath it, and I have UIKit widgets on top of my game. Nirvana.

Dec 282012
 

Not my code, but too awesome not to share. See carlj’s https://gist.github.com/3782351

+ (UIImage*)imageNamedForDevice:(NSString*)name {
    
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        if (([UIScreen mainScreen].bounds.size.height * [UIScreen mainScreen].scale) >= 1136.0f) {
            //Check if is there a path extension or not
            if (name.pathExtension.length) {
                name = [name stringByReplacingOccurrencesOfString: [NSString stringWithFormat:@".%@", name.pathExtension]
                                                       withString: [NSString stringWithFormat:@"-568h@2x.%@", name.pathExtension ] ];
            } else {
                name = [name stringByAppendingString:@"-568h@2x"];
            }
            
            //load the image e.g from disk or cache
            UIImage *image = [UIImage imageNamed: name ];
            if (image) {
                //strange Bug in iOS, the image name have a "@2x" but the scale isn't 2.0f
                return [UIImage imageWithCGImage: image.CGImage scale:2.0f orientation:image.imageOrientation];
            }
        }
    }
    return [UIImage imageNamed: name ];
}

What does it do?

Apple lets you have a Default image name Default-568@2x for the newer 4″ tall iPhones. What if your app needs a specific graphic, say a background image, while running? Easy!

[UIImage imageNamedForDevice:@"background.png"]

And just make sure you have background.png, background@2x.png and background-568@2x.png in your app and you’re good to go!

This has been incorporated into the open source EnkiUtils.

Dec 102012
 

The app I’m working on does not use UIStoryboards, but the previous developer had used ButtonPeoplePicker and when I went to rev BPP I found that it now used UIStoryboards. Hmmm, how can I integrate BPP into my app?

It was easy. First, you load your storyboard:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil];

This gives you a Storyboard object, note the name that ButtonPicker used. That could collide with other names so you should change it to something like BPP_MainStoryboard_iPhone. Then to bring up the BPP all you have to do is:

ButtonPeoplePicker *svc = [storyboard instantiateViewControllerWithIdentifier:@"ButtonPeoplePicker"];

Where the ButtonPeoplePicker name is the “storyboard id” that I assigned with IB. (It was nil in the code I got, presumably because it wasn’t used.) Then you just do the normal:

self presentViewController:svc animated:YES completion:nil];
Nov 202012
 

My Ferret game is using a UITabBarController in part of the UI. Most of the sample code and tutorials I’ve seen have you start with the TabBar template, or have the Tab Bar in the main window. Ferret has a SwitchBoard type icon grid (implemented by Nimbus) and when you press one of the icons you push a view controller that has a tab bar at the bottom.

First time I did this I wrote some pretty awful code. I tried to do the “right” thing, doing the UI in Interface Builder, but I couldn’t hook up each of the Tab Bar’s views in IB, I had to do it programatically. See here?

tabbar

Notice in the Attributes Inspector that I do not have a field for the UIViewController’s nib!

So I wrote code like this:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        MeReadOnlyViewController *meViewController = [[MeReadOnlyViewController alloc] initWithNibName:@"MeReadOnlyViewController" bundle:nil];
        [meViewController setImagePickerVC:self];
		MeFotosViewController *fotosViewController = [[MeFotosViewController alloc] initWithNibName:@"MeFotosViewController" bundle:nil];
		MeBadgesViewController *badgesViewController = [[MeBadgesViewController alloc] initWithNibName:@"MeBadgesViewController" bundle:nil];
		MeFriendsViewController *friendsViewController = [[MeFriendsViewController alloc] initWithNibName:@"MeFriendsViewController" bundle:nil];

		NSArray *array = [[NSArray alloc] initWithObjects:meViewController, fotosViewController, badgesViewController, friendsViewController, nil];
		self.viewControllers = array;

        [self.view addSubview:meViewController.view];
		self.selectedViewController = meViewController;
    }
    return self;
}

Now, that’s not horrible. If you’re not building a UITabBarController from IB you’re going to have to do this. But check out that line,

self.viewControllers = array

What’s up with that?

@interface MeTabViewController : UIViewController <UINavigationControllerDelegate>

@property (nonatomic, retain) NSArray *viewControllers;
@property (nonatomic, retain) UIViewController *selectedViewController;

@property (strong, nonatomic) IBOutlet UITabBar *tabBar;
@property (strong, nonatomic) IBOutlet UITabBarItem *meTab;
@property (strong, nonatomic) IBOutlet UITabBarItem *badgesTab;
@property (strong, nonatomic) IBOutlet UITabBarItem *fotosTab;
@property (strong, nonatomic) IBOutlet UITabBarItem *friendsTab;

@end

Now that’s making me really squirm. Can you say “bad code smell?” I need to create my own NSArray of viewControllers, but why do I need a property for each of the UITabBarItems? Here’s why:

- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
    UIViewController *newViewController;

    self.navigationItem.rightBarButtonItem = nil;

    if (item == meTab_) {
        newViewController = [viewControllers_ objectAtIndex:0];

        self.navigationItem.rightBarButtonItem = [self editButtonItem];
    }else if (item == fotosTab_) {
        newViewController = [viewControllers_ objectAtIndex:1];
    } else if (item == badgesTab_) {
        newViewController = [viewControllers_ objectAtIndex:2];
    }else if (item == friendsTab_) {
        newViewController = [viewControllers_ objectAtIndex:3];
    }

    [self.selectedViewController.view removeFromSuperview];
    [self.view addSubview:newViewController.view];

    self.selectedViewController = newViewController;
}

That’s gross. It works, it works just fine but it seems unmaintainable, fragile, and just bad. I building, by hand, a UIViewController which is really just a UITabBarController. So why not just make one of those? I tried in IB, but I couldn’t hook up the UITabBarController to the view. After struggling a bit, and a bit of google, I decided to just do it by hand, it isn’t that hard.

+ (UITabBarController *) createMeTabBarController
{
    //create a UITabBarController object
    UITabBarController *tabBarController=[[UITabBarController alloc]init];

    // create our view controllers
    MeReadOnlyViewController *meViewController = [[MeReadOnlyViewController alloc] initWithNibName:@"MeReadOnlyViewController" bundle:nil];
    meViewController.tabBarItem.image = [UIImage imageNamed:@"me-profile.png"];
    meViewController.title = @"Me";

    MeFotosViewController *fotosViewController = [[MeFotosViewController alloc] initWithNibName:@"MeFotosViewController" bundle:nil];
    fotosViewController.title = @"Fotos";
    fotosViewController.tabBarItem.image = [UIImage imageNamed:@"me-fotos.png"];

    MeBadgesViewController *badgesViewController = [[MeBadgesViewController alloc] initWithNibName:@"MeBadgesViewController" bundle:nil];
    badgesViewController.title = @"Badges";
    badgesViewController.tabBarItem.image = [UIImage imageNamed:@"me-badges.png"];

    MeFriendsViewController *friendsViewController = [[MeFriendsViewController alloc] initWithNibName:@"MeFriendsViewController" bundle:nil];
    friendsViewController.title = @"Friends";
    friendsViewController.tabBarItem.image = [UIImage imageNamed:@"me-friends.png"];

    tabBarController.viewControllers= [NSArray arrayWithObjects:meViewController, fotosViewController, badgesViewController, friendsViewController, nil];

This eliminates the MeTabViewController class and xib completely. Notice how similar the code is to the first code snippet I showed. I still need to build my array of UIViewControllers but now I must also set the icon and title for the UITabBarItems.

May 202012
 

I just learned something…

Why is the first line better than the 2nd line?

@property (nonatomic, assign) NSObject<ExplanationDelegate> * delegate;
@property (nonatomic, assign) id <ExplanationDelegate> delegate;

It so you can write code like this!

    if([_delegate respondsToSelector:@selector(rightButtonAction:)]) {
        [_delegate rightButtonAction];
    }

If you use id instead of NSObject then you can’t, it won’t compile, you get “No known instance method for selector ‘respondsToSelector:’” which can really have you scratching your head!

Mar 142012
 

I didn’t write this code but it is too awesome not to share. I had an NSString and needed to see if it contained another NSString. I found this post: http://stackoverflow.com/questions/2753956/string-contains-string-in-objective-c

and P i‘s answer is just perfect; make a category on NSString:

@interface NSString ( containsCategory )
- (BOOL) containsString: (NSString*) substring;
@end

@implementation NSString ( containsCategory )

- (BOOL) containsString: (NSString*) substring
{    
    NSRange range = [self rangeOfString : substring];
    BOOL found = ( range.location != NSNotFound );
    return found;
}

@end

This has been incorporated into the open source EnkiUtils.

Feb 022012
 

I noticed that my thumbnails didn’t look sharp on my Retina display. This makes sense since I was asking my server for 50 pixels when in face I wanted 100 pixels. But how to encapsulate that?

I made a quick little method to give me my 50 or 100 dimension:

+ (int) thumbnailSize 
{
    return [EnkiUtilities pixelsForRetina:50];
}

This call the pixelForRetina class method:

+ (float) pixelsForRetina:(float) pixels
{
    if ([[UIScreen mainScreen] enki_isRetina]) {
        return pixels * 2;
    } else {
        return pixels;
    }
}

I really don’t like the hardcoded 2 in there, I really should grab scale from somewhere. And here is the UIScreen class category to help me out with the retina/non-retina distinction.

@implementation UIScreen(EnkiScreenRetinaAdditions)
- (BOOL)enki_isRetina {
    return [self respondsToSelector:@selector(displayLinkWithTarget:selector:)] && (self.scale == 2.0);
}
@end

This has been incorporated into the open source EnkiUtils.