Lessons from iOS dev #2: Delegates are good, blocks are better

One of the core fundamentals of Objective-C is protocols and delegates. Almost every single Objective-C app is likely to use them in some way or another because for many years they have been the ubiquitous way to communicate universally between classes. Whilst they are highly useful and I use them pretty much everywhere, they aren’t always the best solution to the problem.

Keep Calm has two main view controllers (the grid and editing view) and each of them is a delegate for about forty odd different protocols. At least thirty of them are protocols I’ve written myself, rather than Apple ones such as UICollectionViewDataSource. I’ve done a reasonably good job at using #pragma – marks to organise the various methods in the delegates, but the classes are beginning to feel a little bloated.

The new alternative is to use blocks instead. Blocks are essentially an extension of C function pointers. The main benefit is that it massively reduces the amount of code you have to write complete a task:

With a protocol and delegate

@protocol MyClassDelegate

-(void)doSomethingInDelegate;

@end

@interface MyClass : NSObject

@property id<MyClassDelegate> delegate;

-(void)doSomething

@end

@implementation MyClass

-(void)doSomething
{
    [self.delegate doSomethingInDelegate];
}

@end

@interface MyOtherClass<MyClassDelegate> : NSObject
...
@end

@implementation MyOtherClass

-(void)doSomethingInDelegate
{
    NSLog(@"Doing something in delegate");
}

@end

With a block

@interface MyClass : NSObject

-(void)doSomething:(void (^)(void))block;

@end

@implementation MyClass

-(void)doSomething:(void (^)(void))block
{
    block();
}

@end

@implementation MyOtherClass

-(void)begin
{
    [self.myClass doSomething:^(){
        NSLog(@"Doing something in other class");
    }];
}

@end

Obviously the latter example is much shorter and the code is a little easier to read (once you get used to the syntax of blocks). Apple is beginning to use blocks across the Cocoa and Cocoa Touch frameworks for everything from fast iteration to sorting to completion handlers.

There are a lot of cases where blocks aren’t appropriate though – I wouldn’t make a table data source with blocks, for example, because the number of blocks would become ridiculous. In my mind they are best for short snippets of code where they are replacing a protocol that one uses a maximum of two functions. Another disadvantage is that blocks inside blocks produces ridiculously ugly code. Even with only three blocks inside one another my code is comfortably running off the screen in Xcode, which is a pain.

I’ve also got mixed views on how reusable the code produced by blocks is. You are effectively defining functions within functions, which makes it harder to take the source and use it elsewhere because you have to write the block syntax into regular Objective-C function syntax.

Blocks are very useful and its worth learning about them from Apple’s documentation. I’ve only touched on some of the things that you can do with them and I am sure that in the future Apple will carry on introducing them across Cocoa.

Advertisements

Circle Draw for Android

How well can you draw circles? Circle Draw, after having had a few thousand downloads on the App Store, is now available for free on Android via Google Play.

Introducing Keep Calm Creator 2.0

mzl.xdyvzitq

I’ve today updated Keep Calm Creator on the App Store to version 2.0. The new version is highly optimized and most users should find that the app is around 2/3x faster. I’ve also added new features, including support for using emoji as the crown and the you can now see a grid of all your created posters on the iPhone.

mzl.xwihuzpeThe new emoji feature allows you to type in emoji to create a crown based on them.

You can update to Keep Calm Creator 2.0 on the App Store now.

 

Introducing Circle Draw for iOS

circledrawfullAt school you were taught to draw circles with a pencil and a compass. This is great if you need a perfect circle, but how well can you draw a circle with just your finger? Now you can use Circle Draw for iOS to determine how well you can, and a judgmental cat will show your score!

Circle Draw is free for iPad and iPhone. You can get Circle Draw on the App Store now.

An Android version will be available at some point…

 

N-sided shapes

You’ll probably know that the formula for calculating the interior angle of a regular polygon is 180(n – 2) / n where n is the number of sides:

Angle formula

So if we put 4 (i.e. a square) into the formula we 90 degrees, if we stick in 5 (pentagon) we get 108 degrees. Simple. In fact, lets create a JavaScript function to draw these shapes if we enter a starting coordinate, side length and number of sides:

 function drawShape(c, startX, startY, sideLength, sideCount)
 {
  var interiorAngleDegrees = (180 * (sideCount - 2)) / sideCount;
  var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; //Convert to radians
  c.save();
  c.beginPath();
  c.translate(startX, startY);
  c.moveTo(0,0);
  for (var i = 0; i < sideCount; i++)
  {
    c.lineTo(sideLength,0);
    c.translate(sideLength,0);
    c.rotate(interiorAngle);
  }
  c.stroke();
  c.restore();
}
Example of a square

Example of a square drawn with function

Now, if what happens if we take our original formula and stick in something that isn’t an integer. If we put in 2.5 we get out 54 degrees. What does a 2.5 sided shape look like with the above function?

A '2.5' sided shape.

A ‘2.5’ sided shape.

Clearly this isn’t a full polygon, so perhaps it is better to think of 2.5 as 5/2. Let’s adapt the function a little:

function drawShape(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator)
 {
   var sideCount = sideCountNumerator * sideCountDenominator;
   var decimalSides = sideCountNumerator / sideCountDenominator;
   var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides;
   var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; //Convert to radians
   c.save();
   c.beginPath();
   c.translate(startX, startY);
   c.moveTo(0,0);
   for (var i = 0; i < sideCount; i++)
   {
     c.lineTo(sideLength,0);
     c.translate(sideLength,0);
     c.rotate(interiorAngle);
   }
   c.stroke();
   c.restore();
 }

Now the function can draw 5 / 2 shapes:

A 5 / 2 sided regular polygon.

A 5 / 2 sided regular polygon.

If you want to check out a full source code example, you can see it here.

Stereoscopic 3D on iOS

Stereoscopic  3D

iOS devices don’t have any kind of 3D option built in, which I’m glad of because it is a pointless gimmick that gives me headaches. Having said that, I quite like the ‘retro’ stereoscopic 3D that could be achieved with red-cyan glasses.

The premise of 3D images is really simple: you have two different images taken it two different positions that are roughly eye distance apart and you then have to find a way of making sure each image only gets into one eye.

About three months ago I was learning the basics of OpenGL ES and I created a dumb Minecraft style world:

'Minecraft' style world

I actually got it pretty well optimised and it can run happily at about 60fps. I then began to look at the code this morning and figured out that stereoscopic rendering wouldn’t be that hard to add to it.

The method is actually really simple:

  • Create two offscreen textures and associated frame buffers* that are the same dimensions as your view.
  • On each frame create two view matrices from your original view matrix with each one shifted a little (I went for about 5mm on an iPad screen)
  • Render the separate view matrices twice into the associated offscreen textures
  • Present both textures blended together with red and blue

The end result (this is the same view as above) looks a bit like this:

iOS Simulator Screen shot 1 Apr 2013 18.00.17

There are a number of disadvantages to this technique. The first is that you can’t go to really high resolutions. I got this running at 60fps on a retina iPad, but I had to render at 1024 * 768 (rather than native 2048 * 1536) without anti-aliasing. The second is that you lose a lot of color information; I had to grayscale my images and even then they appeared quite dull compared to the original image. The third is that this doesn’t scale well across devices because of the distance between the left/red camera and the right/blue camera. I added a pinch to change distance feature so that I could compare between the iOS simulator and real devices.

Despite this, it is actually quite a cool technique although I don’t think that it will become particularly mainstream yet.

iOS Simulator Screen shot 1 Apr 2013 18.16.53 iOS Simulator Screen shot 1 Apr 2013 18.16.58


Update: After some discussion on Reddit I’ve now updated the source a little so that the blending is a little different: the final pixel is made up of the red component of the left pixel and the green and blue components of the right pixel. This maintains color and produces brighter images:

As per request, I’ve also stuck the code on GitHub. It is a little verbose at the moment, but it’s still readable.

*Oh yeah, no GLKit here!