Secret Lab

We convert coffee into games.

Tag: iOS

Using Gesture Recognizers with Cocos2D and CCNode + SFGestureRecognizer

by Jon Manning

One of the most interesting and useful features that iOS includes is the gesture recogniser. Gesture recognisers are objects that are attached to views, and look for specific patterns of touches. When a gesture recogniser notices that the user has interacted in the way that it’s looking for, it notifies a delegate.

Prior to gesture recognisers, handling complex gestures like pinching or rotation was a lot harder than it had to be. Time was, developers had to manually track the touches involved in a gesture, and measure how they were moving over time; nowadays, we just do this:

UIRotationGestureRecognizer* rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotated:)];
[self addGestureRecognizer:rotation];

And then have a method that gets run when the user interacts with the view with a rotation gesture:

- (void) rotated:(UIRotationGestureRecognizer*)rotation {
  if (rotated.state == UIGestureRecognizerStateChanged) {
       // the rotation gesture has changed, do something about it
  }
}

Gesture recognisers are one of those APIs that are completely obvious once you think about them, and solve a potentially tricky problem very cleanly. However, gesture recognisers have to work within the bounds of how views in iOS work, which can have some interesting consequences for using them in games.

To put it briefly: how can we use gesture recognisers in OpenGL-based games?

Here’s the problem: gesture recognisers work by being attached to views; when a touch lands on the screen, UIKit determines which view the finger belongs to, and this information is used for tracking gestures.

However, all OpenGL games do their main work using a single view – the OpenGL view in which all rendering takes place. This is true regardless of whether you’re drawing complex 3D graphics or simple 2D sprites. And this can mean that gesture recognisers are trickier to do, because when the finger lands on the screen, UIKit will say, “hey, the view that was touched was the OpenGL view! Job done, you’re welcome, see you later!”

So, if we want gesture recognisers, and we’re drawing using a single OpenGL view, what needs to happen is this that gesture recognisers need to be added to the OpenGL view, but are limit the areas in which they’ll look for touches to areas that depend on what’s happening in the game.

This is possible through the use of the gestureRecognizer:shouldReceiveTouch: method in the UIGestureRecognizerDelegate protocol. If a delegate implements this method, it’s possible to make a recogniser only track touches in certain areas.

This is the approach taken by Krzysztof Zabłocki’s CCNode+SFGestureRecognizers, which is a zlib-licensed extension to Cocos2D. CCNode+SFGestureRecognizers performs some very clever hacks, including dynamically creating classes that operate as delegates and using the Objective-C runtime’s new associated object support, that allow you to add gesture recognisers directly to CCNodes.

We’re using CCNode+SFGestureRecognizers in Leonardo’s Moon Ship, an adventure game that we’re looking forward to talking about further in the coming weeks and months, to support dragging items from the player’s inventory onto items in the game world.

leo-dragging

iOS Developer Lab: February 2013

by Doctor Science

This event has now passed! Stay tuned for our next training workshop!

Secret Lab iOS Developer Lab in Sydney –– 22, 23, 24 February 2013

For more information, or to register, visit the event page.

Science Week (Tasmania)

by Paris Buttfield-Addison

We’re very pleased to announce the availability of the official iPhone app for the Tasmanian events of the National Science Week 2012 festival. National Science Week is Australia’s largest national festival and includes hundreds of events held all over Australia.

National Science Week 2012 Tasmanian Events –– iPhone app

The app features:

  • full program of National Science Week events in Tasmania
  • day by day Event Calendar
  • maps showing the location of events
  • search by region or current location
  • automatic schedule updates

Grab the app on the iTunes App Store, and please let us know what you think!

Using JSON to load Objective-C objects

by Jon Manning

You want to write an iOS or OS X app that quickly and easily retrieves some data from a server and converts it to an Objective-C class. Also, you want to write the server using something standard, like Django or Ruby on Rails.

The standard way that you’d do this would be using NSCoder, where your client application would receive the data and you’d unpack it like so:

SomeObject* myObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

However, the file format for serialised objects is internal to iOS and OS X, and is also a binary format that’s very difficult to read while in transit.

We recently had a similar problem, where we wanted to have our server send data in JSON format, which we like because JSON’s pretty human-readable. We could implement our own custom implementation of NSCoder, but we were after a more lightweight solution.

Key-value coding

This is where key-value coding comes in. Key-value coding is a feature of Cocoa that lets you access properties and instance variables of class at runtime by name, rather than simply at compile time.

For example, if you had a class that had an NSString property called “name”, you could access it like this:

NSString* name = [myObject name];

Or you could access it with key-value coding, like this:

NSString* name = [myObject valueForKey:@"name"];

Likewise, you can set the name property like this:

[myObject setName:@"Bob"];

But you could also set it using key-value coding, like this:

[myObject setValue:@"Bob" forKey:@"name"];

The advantage of key-value coding is that you can generate the keys at run-time. Your code doesn’t have to know about the properties and variables kept inside an object in order to attempt to get and set the values.

This is where our lightweight serialisation comes in. Let’s say that we’ve received some JSON data that looks like this:

{
    "name":"Bob",
    "income":75000,
    "title":"Janitor"
}

In this example, we’ve also got an Objective-C object that has matching properties:

@interface Employee
	
@property (strong) NSString* name;
@property (assign) int income;
@property (strong) NSString* title;

@end

Converting the JSON data into a dictionary is pretty easy.

NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:nil];

You can then create the Objective-C class from this dictionary by creating a new Employee object, and then iterating over each of the keys in the dictionary, using setValue:forKey: to set the values.

Employee* employee = [[Employee alloc] init];
	
for (NSString* key in dictionary) {
    [employee setValue:[dictionary valueForKey:key] forKey:key];
}

This works even for non-object properties like Employee’s income property, which is an int. In this case, the dictionary loaded from the JSON contains an NSNumber object for the income field; when setValue:forKey: is used for that key, it automatically extracts the int from the NSNumber and applies it to the class.

There’s an even easier way to set all of the values:

[employee setValuesForKeysWithDictionary:dictionary]

Loading objects when you don’t know the class

Another possible use case is one where you receive a chunk of JSON, but you don’t know ahead of time what kind of object the JSON should be turned into. This is where NSClassFromString comes in handy.

If your JSON object contains a field called, say, class, you can use that to create the empty object. Like so:

Class ObjectClass = NSClassFromString([dictionary objectForKey:@"class"]);
	
if (ObjectClass == nil) {
    // the class doesn't exist; give up
    return;
}
	
NSObject* loadedObject = [[ObjectClass alloc] init];

// Load the properties as above

The reason we use NSObject in the above example is because all NSObjects support the key-value coding methods. Even though loadedObject is treated as a generic NSObject, the fact that it was created with whatever class ObjectClass is determined to be means that the object will be what you specify.

Dangers and Caveats

This technique only applies for simple properties, like strings and numbers. More complex data types or references to other Objective-C objects need to be handled differently.

If you use setValue:forKey: on a key that doesn’t exist in the target object, the object will throw an exception and crash. There are a couple of ways you can handle this; one is to implement the setValue:forUndefinedKey: method in your classes.

This method is called when you try to set a value for a property or instance variable that doesn’t exist. The default implementation throws an exception and crashes; you can implement your own that leaves a warning, for example.

A final note: this is a very quick-and-dirty solution. If you want more reliable behaviour, you’ll need to add more checks and validation code. For simply loading objects, it works pretty well.

Android-Fu: Awesome Apps for Ice Cream Sandwich and Beyond

by Paris Buttfield-Addison

At OSCON this year, @chrisjrn, @desplesda and I presented a 3-hour tutorial on “Android-Fu: Awesome Apps for Ice Cream Sandwich and Beyond“. We had a great turn out, and it was a lot of fun to present at OSCON for the second year in a row (last year we presented the well received Android for people who hate phones, a similar topic in a similar style).

Secret Lab's OSCON 2012 presentation

You can also find the code from the activity on GitHub, as well as the final binary (APK) for installation on your Android 4.0+ device.

We hope you enjoyed it! Please get in touch if you have any questions or feedback!

Using OmniGraffle as a level editor

by Doctor Science

This was a fun one.

As we’ve mentioned before, we’re currently working on a board game for a client. Without going into the details, the game is based on nodes on a game board that are linked together; players move between connected nodes, and claim territory.

When coding up the game, we initially generated the game boards in memory, by directly instantiating ‘node’ and ‘link’ objects. Then, when it came time to make the game load different boards on demand, we created a JSON-based data structure that defines the nodes and links. It looks a little something like this:

	{
	  "nodes": [
	    {
	      "id": "Node-84578",
	      "positionX": 423,
	      "positionY": 424
	    },
	    {
	      "id": "Node-10710",
	      "positionX": 198,
	      "positionY": 424
	    }
	  ],
	  "links": [
	    {
	      "nodes": [
	        "Node-84578",
	        "Node-10710"
	      ],
	      "identifier": "Relationship-84578-10710"
	    }	    
	  ]
	}

This structure defines two nodes, including their names and positions, as well as a relationship that links the two together. (Because the graph is nondirectional, we didn’t want to define links in the node data structure itself.)

This is all well and good for simple game boards, like this:

But then the client said, “cool, can you make this?”

And we were all:

Clearly, hand-coding wasn’t an option, and a tool was needed. Writing one ourselves wouldn’t have been the best use of our time, so we looked at our favourite Mac app ever: OmniGraffle Pro. OmniGraffle already knows about objects and connections, and it exposes all of this information through AppleScript.

So, we went searching, found this excellent Gist by Michael Bianco, and adapted it into one that extracts the information we need and generates the game board data that we care about.

Loosely put, it turns this:

Into a level that can be loaded:

How it works

First, we designed the level. In this board game, there are only nodes and connections; we represented nodes as circles, and connected the nodes with lines.

The first thing the script does, we get access to OmniGraffle via the Scripting Bridge:

	# We use the json gem to render the output
	require 'rubygems'
	require 'json'

	# Import the scripting bridge so we can communicate with OmniGraffle
	require 'osx/cocoa'
	include OSX
	OSX.require_framework 'ScriptingBridge'

	# Replace "OmniGrafflePro" with "OmniGraffle" if you don't have Pro
	graffle = SBApplication.applicationWithBundleIdentifier_("com.omnigroup.OmniGrafflePro")

OmniGraffle exposes a list of shapes, which AppleScript can query for important information such as the origin point, and its text. You can also query and set a special “tag” property for each shape, which is useful for storing an identifier. We base the identifier on the text of the shape, if it has any; otherwise, we use a random number.

So, to generate the list of game nodes, we ask OmniGraffle for all shapes, and format the result into a hash, which we store in an array for later.

When generating the hash for a node, we can also make use of the user data dictionary that OmniGraffle Pro exposes. This lets you set custom key-value pairs for a shape, which is very useful for setting things like which player owns a node, or at what point in the game the node becomes active. This is a simple matter of merging in the userData hash.

	$shape_list = []

	graffle.windows[0].document.canvases[0].layers[0].shapes.select do |s|  

	  text = s.text.get.to_s
	  if text == "":
	    text = rand(100000).to_s
	  end
  
	  s.tag = text
  
	  $shape_list << {
		:positionX => s.origin.x.to_i, 
		:positionY => s.origin.y.to_i, 
		:id => "Node-" + s.tag}.merge(s.userData)
		
	end

Once all nodes have been processed, we know that all shapes have had a tag associated with them; we can then iterate over all shapes a second time, this time generating information for each connection.

	graffle.windows[0].document.canvases[0].layers[0].shapes.select do |s|

	  s.outgoingLines.each do |l|
    
	    l.tag = "Relationship-" + l.source.tag.to_s + "-" + l.destination.tag.to_s
    
	    $line_list << {:identifier => l.tag, :nodes => ["Node-" + l.source.tag, "Node-" + l.destination.tag]}
	  end
	
	end

Finally, we export the nodes and links as JSON:

	json_rep = {:nodes => $shape_list, :links => $line_list}
	puts JSON.pretty_generate json_rep

Because this script operates on the frontmost open document in OmniGraffle and outputs to stdout, saving the JSON is as simple as a one-line command:

	$ ruby graffle2board.rb > MyAwesomeBoard.board

Summary

This is a pretty powerful technique, since it lets us design the game maps with a powerful (and, more importantly, pre-existing) tool and import them exactly the way we want them. We’re definitely going to be using this more in the future.

You can see the script in GitHub!

UIView+Glow: Fancy Glowing Effects for Everyone

by Jon Manning

We recently needed to add a tutorial to a board game (see SLTutorialController), and realised that we needed a way to highlight various controls and other user interface elements that the user should interact with next. A common way that this is handled is by making things glow, often with an animation.

So, we wrote UIView+Glow. It’s a very simple category that adds two methods: startGlowing and stopGlowing. When you call startGlowing, the view will start to pulse with a soft light; this effect is removed when stopGlowing is called.

You can see a video of it in action here:


It’s available on our GitHub now!

Embedding External Nibs for Fun and Profit

by Jon Manning

Here’s a common use case we run across.

We’re building a really nice custom control, and it’s got a reasonably complex view hierarchy. We want to be able to have multiple copies of it, plus keep the complexity of the higher-level UI down, so we store it in a nib.

The only way you can get stuff out of a nib is to use the UINib API to load the nib, get the loaded object, and then start using it. This means writing code. We want to keep the amount of code in the app down, and programatically generating interfaces sucks. What we want to do is to just insert a UIView placeholder into our view controller, and have it be replaced with the full control at runtime.

So, how can we do this?

Some previous work on this topic was done by Yang Meyer, who figured out that you can override the -awakeAfterUsingCoder: method to switch out the placeholder view with a view loaded from a nib at load-time. However, this method doesn’t play nice with ARC.

We came up with a solution that we quite like. It’s easy to understand, allows us to keep the control’s UI in a separate nib, and also allows us to simply insert an empty UIView into our view controllers (and not clutter them up).

We used this technique in the development of SLNumberPickerView.

The Technique

First, design the interface for your class in a separate nib. We find it helpful to write a class method that loads and returns the object:

// Load the nib and return the newly-created picker view.
+ (MyAwesomeObject*)loadFromNib {
  UINib* nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
  NSArray* objects = [nib instantiateWithOwner:self options:nil];
  return [objects objectAtIndex:0];
}

Next, we override -awakeAfterUsingCoder: to check to see if self is a placeholder view or the real view that was loaded from the external nib. We determine this based on how many subviews we have – if it’s zero, then we’re the empty placeholder view.

If we figure out that self is the placeholder, we load a new instance of the view, and then add it as a subview of the placeholder. We also keep a reference to this internal view, and forward any relevant messages to it.

- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {

  // We are a stand-in view if we have zero sub-views.
  BOOL wasPlaceholder = ([[self subviews] count] == 0);

  if (wasPlaceholder) {
    
    // Load the real view, and add it as a subview of ourself.
    // Also mark ourself as a stand-in view, so that we know 
    // to forward hit tests and requests for properties.
    _internalView = [MyAwesomeObject loadFromNib];
    _internalView.frame = self.bounds;
    _isStandIn = YES;
    [self addSubview:_internalPickerView];

    // Set the background to clear, so that at design-time we 
    // can use an opaque color to help identify this custom view.
    self.backgroundColor = [UIColor clearColor];
  }

  return self;
}

There’s one drawback to this technique: because we’re inserting the real view inside the placeholder view, we’re keeping an extra instance of the class around (though none of its subviews are loaded, so not too much additional memory is allocated.)

You can see an example of this technique in action in SLNumberPickerView, available on our GitHub.

WWDC 2012

by Jon Manning

Another keynote has come and gone, and this one was one of the most emotionally charged and content-packed of recent memory. One of the things that struck me from CEO Tim Cook’s opening and closing remarks was the sheer amount of emotion in his voice when he began describing how the iPad and iPhone have changed people’s lives for the better.

When you combine this with some other key features of both iOS and OS X that Apple heavily pushed at developers today, you can start to see a common theme emerging – Apple wants to continue the tradition of making the world a better place, in their eyes. When Steve Jobs was CEO, this meant making the world a better place in his eyes, and doing so under his personal criteria: better design, ease of use, and making computers fit into their place in user’s lives. Tim Cook’s approach, however, appears much more heavily focused on globally-beneficial improvements. Almost ten minutes was spent talking about how VoiceOver helps the blind, how the iPad and AirPlay improve teaching, and how apps like Airbnb help people connect.

The common theme here is helping, and it’s easy to see what Apple – or at least Cook – see as the purpose of the devices they sell. They should help people with something.

After the keynote, Apple began the first of their confidential sessions. While I can’t relate details, they spent significant time on the new accessibility features and hammered home the point that accessibility is important. There are more and more users, and this means more and more people to help – and more and more ways in which software can find ways to improve their lives.

That’s my justification for spending half our funds on new MacBook Pros, anyway.

SLNumberPickerView

by Paris Buttfield-Addison

SLNumberPickerView is a control that presents a 3-digit number picker. It’s very customisable, highly themable, and very easy to use.

We made SLNumberPickerView after a designer, the marvellous @padschneider, crafted this look for a number picker:

Our first reaction: “argh, UIPickerView can’t be made to look like that, no matter how many UIView hierarchy hacks you throw at it!”

Our second reaction: “hey, UIPickerView sucks anyway, let’s make a better version for this problem!”

So, here’s SLNumberPickerView. The image above it isn’t from our PSDs, it’s a screenshot from the working thing in the simulator.

It does one thing: present a suitcase-combination-lock-style interface to the user, which lets the user choose three digits. You can then query the view to get the integer value of the number the user’s chosen. SLNumberPickerView also provides a delegate protocol that you can implement to be notified of any changes.

SLNumberPickerView keeps its own view hierarchy in a separate nib, which makes it very easy to theme – you can change the background colours, overlay images, and do all kinds of things with it.

SLNumberPickerView allows you to control the colour and font of the numerals, and also allows you to specify a tiled background image that is placed beneath the numerals. We added this in order to add the tick-marks that you see at the right of each column in the above screenshot.

How to use it

Download SLNumberPickerView from GitHub, and add the files to your project. SLNumberPickerView requires ARC.

Next, add an empty UIView to your interface. Size it to something like 145 wide by 100 high, and set its class to SLNumberPickerView. At runtime, the number picker view will be placed inside it.

You can query the number picker view for its value via the read-only value property:

NSInteger selectedNumber = myNumberPicker.value;

Alternatively, you can conform to the SLNumberPickerViewDelegate protocol, which defines the following method:

- (void) numberPickerViewDidChangeValue:(SLNumberPickerView*)picker;

Set yourself to be the delegate, and you’ll receive messages every time the number selected by the user changes.

Caveats

SLNumberPickerView currently doesn’t support setting a value programmatically.

It’s available on GitHub now.

Follow

Get every new post delivered to your Inbox.