Aug 302013
 

Lets say you have an app that you don’t quite know what some parameters should be. This isn’t that uncommon, it might be as simple as not knowing what a default should be, or maybe you’ll have a list of in-app content to sell. The possibilities are pretty huge.

This is surprisingly easy to do. First, make a JSON file and host it somewhere.

{ "ShadowHeight":"20" }

Make a property to hold this value and initialize it.

@property(nonatomic, assign) int shadowHeight;
self.shadowHeight = 15;

Next you need to start a background query to get that file from the net.

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://yourdomain.com/yourfile.json"]];
        [self performSelectorOnMainThread:@selector(parseJSONFile:)
                               withObject:data waitUntilDone:NO];
    });

Your app will run with the default value of 15 until the net query returns. When it does, the parseJSONFile: selector is called.

- (void)parseJSONFile:(NSData *)responseData {
    NSError* error;
    if (responseData) {        
        NSDictionary* json = [NSJSONSerialization
                              JSONObjectWithData:responseData
                              options:kNilOptions
                              error:&error];
        
        NSNumber *shadowHeight = [json objectForKey:@"ShadowHeight"];
        self.shadowHeight = [shadowHeight integerValue];
    }
}

And that’s it. We run with the compiled in value and switch to the runtime value when (or if) the file is served by the host. It couldn’t be easier.

Aug 302013
 

I’ve released 2 apps both with iAds. I used Apple’s BannerView sample code to implement this. Basically, in your delegate you don’t set root to your expected root UIViewController but rather you set root to a BannerView which contains your real root. When an iAd is available, your main view shrinks and the iAd is displayed at the bottom. When an ad isn’t available, your view expands to its “normal” size.

This worked very well in testing so I released both apps to the app store. They’re Done Yet? and Wheelin. However, when I first downloaded the versions from the store I was quite surprised to see no ads ever. It turns out that at least right now, iAd had a pretty horrible fill rate. So I wanted to show another ad when an iAd wasn’t available.

I found LARSAdController, an open source project by larsacus on GitHub. He makes ad integration very easy except for one thing. When you go down his quick development route you get the ads covering your view, it doesn’t shrink to accommodate  the ad. This is a completely reasonable design decision, just not one I wanted.

So I decided to modify Apple’s BannerView to use LARSAdController. It was pretty easy.

The first thing you do is remove iAd from BannerView’s .h file and ad in the LARS TOLAdViewController class.

#import "TOLAdViewController.h"

#define KVO_AD_VISIBLE @"KVO_AD_VISIBLE"

@interface BannerViewController : TOLAdViewController

(Just ignore the KVO_AD_VISIBLE define for now, I’ll cover that later.) In the .m file also remove iAd and make these changes:

@implementation BannerViewController {
    UIView *_bannerView;
    UIViewController *_contentController;
    BOOL isLoaded;
}

We changed _bannerView from an ADBannerView into a plain old UIVIew. ADBannerView also has a bannerLoaded property which we’ll have to replace with our isLoaded boolean. initWithContentViewController is also easy to modify.

        // IAPHelper *sharedInstance = [//IAPHelper sharedInstance];
        //if ([sharedInstance showBannerAds]) {
        if (YES) {
            _bannerView = [[UIView alloc] initWithFrame:CGRectZero];
        } else {
            _bannerView = nil;      // not showing ads since the user has upgraded
        }

Notice the commented out section. If you are using in-app purchases to transform an ad supported version into an ad free version you can do that right there.

At the end of the method well use Key-Value-Observing (KVO) to watch LARS and see when an ad is served or removed. (I’ll probably cover KVO in a future blog entry.)

    [[LARSAdController sharedManager] addObserver:self
                                       forKeyPath:kLARSAdObserverKeyPathIsAdVisible
                                          options:0
                                          context:KVO_AD_VISIBLE];

And the observing code:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context;
{
    if(context == KVO_AD_VISIBLE) {
        NSNumber *isVisible = [object valueForKey:kLARSAdObserverKeyPathIsAdVisible];

        if ([isVisible boolValue]) {
            _bannerView.frame = [[LARSAdController sharedManager] containerView].frame;
            isLoaded = YES;
        } else {
            isLoaded = NO;
        }
    }

    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}

We save the frame of the new ad and also update the isLoaded variable. (Originally thought I would need to call setNeedsLayout and layoutIfNeeded but in practice I didn’t.) The mods to viewDidLayoutSubviews were also pretty straightforward. The only odd part was removing the references to ADBannerContentSizeIdentifierPortrait and ADBannerContentSizeIdentifierLandscape and just replacing that all with a single line:

    bannerFrame.size = [_bannerView sizeThatFits:contentFrame.size];

And a few lines later you use the new isLoaded variable

    if (isLoaded) {

Don’t forget to clean up your observer in dealloc:

-(void) dealloc;
{
    [[LARSAdController sharedManager]  removeObserver:self forKeyPath:kLARSAdObserverKeyPathIsAdVisible];
}

All that remains is to tell LARS about your ads in your app delegate:

        [[LARSAdController sharedManager] registerAdClass:[TOLAdAdapterGoogleAds class] withPublisherId:@"a14e55c99c24b43"];
        [[LARSAdController sharedManager] registerAdClass:[TOLAdAdapteriAds class]];

        LARSBannerViewController *root = [[LARSBannerViewController alloc] initWithNibName:@"LARSBannerViewController" bundle:nil];

        _bannerViewController = [[BannerViewController alloc] initWithContentViewController:root];

        [self.window setRootViewController:_bannerViewController];

And that’s it. Now your app should show iAD or AdMob ads and your view will shrink to accommodate them.

Of course there’s a bug, I don’t know if this is in AdMob server or in LARS but when you are in Landscape mode on an iPhone the ad’s size and the reported size are different leaving a black bar at the bottom of the screen. I’ve pinged larsacus about it and will update this post when I know more.

I’ve forked LARSAdController and submitted the above sample code in a full project on my github.

Aug 032013
 

I recently had a recruiter ask me if I had experience with UICollectionView. Coincidentally, I had just read about that a day or two before. Now, that’s not really experience that gets you the gig so I threw together a sample app that used UICollectionView.

The app, Image Fool, implements an unusual, and not very useful, way of browsing images on Flickr. When first launched, it shows 26 images, the first of which matches the Flickr image search for ‘a’ and the second for ‘b’ and so on.

imageFool

The problem I had was that it was slow, I mean, really slow. To show the 26 images I need to hit the Flickr server 26 times fast.

The first thing I tried was the standard Grand Central Dispatch method of putting your slow operations on a thread and then doing your UI work on the main thread.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // switch to a background thread and perform your expensive operation
    dispatch_async(dispatch_get_main_queue(), ^{
        // switch back to the main thread to update your UI
    });
});

This code snippet is from Ray Wenderlich’s wonderful iOS blog. I like how with just 2 comments the author, Marcelo Fabri,  tells us what goes in the first part and what goes in the 2nd dispatch_async.

I immediately realized I wanted finer control over my threads, both the number of concurrent operations and their priority. That meant I should use NSOperation.

for (int i = 0; i < kIMAGE_FOOL_MAX_CELLS; i++) {
        __weak IFViewController *weakSelf = self;
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            IFFlickrPhoto *newFlickr = [[IFFlickrPhoto alloc] init];
            [newFlickr loadFlickrPhotos:searchString];
            [self.collectionView reloadData];
        }];

        [operation setQueuePriority:NSOperationQueuePriorityVeryLow];
        [self.flickrImageQueue addOperation:operation];
}

And when I setup the queue I told it to perform only 2 concurrent operations:

    self.flickrSearchQueue = [[NSOperationQueue alloc] init];
    self.flickrSearchQueue.maxConcurrentOperationCount = 2;

Performance was still bad. After a lot of head scratching I realized that my call to reloadData wasn’t happening at the right time. I tried putting the reloadData onto the main thread.

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
});

And it was fast enough but each image was redrawing like crazy over and over again. Image flicker in a Flickr program is not desirable.

And then the lightbulb went on, I was asking it 26 times to reload the data, that was ludicrous. I needed 26 cells to reload, but each of them only once. That’s where the reloadItemsAtIndexPaths method is useful

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
});

Of course I had to figure out the indexPaths but that’s easy in this case, I have a for loop and the index path just contains the index of the for loop.

And now the UICollectionView cells scroll smoothly. You can download the full source from github.

Jul 232013
 

I’m working on an exercise timer in my spare time. The design called for different UI for portrait and landscape and both rubber banding and auto-layout just wasn’t going to work. Objects moved when I rotated the device.

topologically

Notice how the green box is below the magenta box in the portrait version, yet above it and to the left of the orange box in the landscape version.

So I tried using multiple xib files, one for portrait and one for landscape. I’ve done this before and things were fine but that was awhile ago. With iOS6 things were not fine.

ios6

Stackoverflow.com is a great resource for tricky situations just like this, so I asked there about this. And if you go there you’ll see that my question was not answered. I had a friend help me out, he used 2 views in one xib file with IBOutlets for portrait and landscape view and he toggled between them the device rotated. Perfect, right? Well, no, when you have 2 views in a XIB you can’t hook up your IBOutlets to both places. I had it working visually but my controls only worked in one orientation.

I eventually came up with the idea of using a orientation master view controller that loaded container view controllers when the device rotated. That worked fine. Lets look at the code:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
    if (_timerViewController) {
        [_timerViewController.view removeFromSuperview];
        [_timerViewController willMoveToParentViewController:nil];
        [_timerViewController removeFromParentViewController];
        self.timerViewController = nil;
    }
    
    self.timerViewController = [[XTMViewController alloc] initWithNibName:
          [self xibNameForDeviceAndRotation:interfaceOrientation withClass:[XTMViewController class]]
                                             bundle:nil];
    
    // use bounds not frame since frame doesn't take the status bar into account
    _timerViewController.view.frame = _timerViewController.view.bounds = self.view.bounds;
    
    [self addChildViewController:_timerViewController];
    [_timerViewController didMoveToParentViewController:self];
    [self.view addSubview: _timerViewController.view];
}

The addChildViewController and didMoveToParentViewController should be familiar if you read my previous blog entry on Container View Controllers. There are two things to notice above those calls though. I’ll deal with the second one first, I set the child view controller’s frame and bounds from the parents bounds, not frame. This is to take account of the status bar.

And notice the call to xibNameForDeviceAndRotation to load the view controller from its xib file. Lets look at that code:

- (NSString *) xibNameForDeviceAndRotation:(UIInterfaceOrientation)toInterfaceOrientation withClass:(Class) class;
{
    NSString *xibName ;
    NSString *deviceName ;

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        deviceName = @"iPad";
    } else {
        deviceName = @"iPhone";
    }

    if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )  {
        xibName = [NSString stringWithFormat:@"%@-Landscape", NSStringFromClass(class)];
        if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
            return xibName;
        } else {
            xibName = [NSString stringWithFormat:@"%@_%@-Landscape", NSStringFromClass(class), deviceName];
            if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
                return xibName;
            } else {
                NSAssert(FALSE, @"Missing xib");
                return nil;
            }
        }
    } else {
        xibName = [NSString stringWithFormat:@"%@", NSStringFromClass(class)];
        if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
            return xibName;
        } else {
            xibName = [NSString stringWithFormat:@"%@_%@", NSStringFromClass(class), deviceName];
            if([[NSBundle mainBundle] pathForResource:xibName ofType:@"nib"] != nil) {
                return xibName;
            } else {
                NSAssert(FALSE, @"Missing xib");
                return nil;
            }
        }
    }
    return nil;
}

There’s a lot going on here. Let’s go over it. I first determine if you are on an iPhone or an iPad. The xib files will have iPhone or iPad in their names. Next we check to see if we are in landscape mode. If we are, we build a test string from the class name, using class reflection via NSStringFromClass. Next, we use pathForResource to check to see if the xib exists in our bundle. If it does, we return the xib name. If it doesn’t, we try again also putting the device name into the xib name. Return it if it exists, assert a failure if it doesn’t. Portrait is similar except by convention we don’t put “-Portrait” into the xib name.

This code is useful enough and generic enough that I’ll put it in my EnkiUtils open source project.

Since this is iOS6 we need to put in the iOS6 rotation boilerplate code:

- (BOOL) shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAll;
}

Curiously we also need to manually call willAnimateRotationToInterfaceOrientation on iPads. iPhones get a willAnimateRotationToInterfaceOrientation automatically but iPads do not.

- (void) viewDidAppear:(BOOL)animated
{
    // iPad's don't send a willAnimate on launch...
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        [self willAnimateRotationToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] duration:0];
    }
}

So, are we finished? Embarrassingly no. You see, when I coded the XTMViewController class I broke the Model-View-Controller design pattern! This is easy to do, Apple already helps us by putting the View and the Controller into the same class. And it is so easy to carelessly mix in Model data in the VC’s .h file. And I had done exactly that. When I run the above code it work brilliantly, I could rotate it all day and the UI was correct in both orientations. But what do you think happened when I rotated the device while my exercise timers were running? Yup, they were all deleted and the UI reset to the initial state. This was not at all what I wanted!

I made a XTMUser class to hold all the timing data, I put all the NSTimers into the  XTMOrientationMasterViewController class and then I made a protocol so the XTMOrientationMasterViewController could respond to UI taps in the XTMViewController class.

Then I was done.

landscape portrait

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 haven’t 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 will 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];

    [[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. Notice 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];