Cocos2D Buttons Tutorial for iOS: How To Create Buttons in Cocos2D: Simple, Radio, and Toggle

Ray Wenderlich
Buttons Tutorial Screenshot

Buttons Tutorial Screenshot

When you are making a game in Cocos2D, likely one of the first things you’ll find yourself needing is buttons. This Cocos2D buttons tutorial will show you how to create buttons in Cocos2D step by step, starting with simple buttons and then covering toggle and radio buttons too. This Cocos2D buttons tutorial assumes that you’ve already gone through the tutorial on how to create a simple game with Cocos2D, or have equivalent knowledge.

When I first started trying to add a button with Cocos2D, I thought the best way to was to just create a sprite representing the button and check for when the button was tapped. Although this is definitely possible, there’s a much easier way to create buttons in Cocos2D – by using the Cocos2D menu system.

The Cocos2D menu system consists of a menu that has a number of menu items inside it. Menu items can be either text or images, and the menu system contains logic for arranging menu items, highlighting items when they are tapped, toggling items, and more. So let’s give it a shot and try to create a simple button the Cocos2D way!

Creating A Simple Button

Create a new project in XCode using the cocos2d Application template, and name your project “CCButtons.” Next you need some images of buttons to work with – you can either use your own, or download some button images I have created. Once you have the images, drag them into your resources folder and make sure “Copy items into destination group’s folder (if needed)” is checked.

Open up HelloWorldScene.h under Classes and add a member variable to the HelloWorld class, we’ll need this in a bit:

CCLabelTTF *_label;

Then before we forget go to HelloWorldScene.m and add the cleanup code to the dealloc method:

[_label release];
_label = nil;

Ok now for the good stuff. In the same file (HelloWorldScene.m), replace the init method with the following code:

-(id) init
{
  if( (self=[super init] )) {
 
    CGSize winSize = [[CCDirector sharedDirector] winSize];
 
    // Create a label for display purposes
    _label = [[CCLabelTTF labelWithString:@"Last button: None" 
      dimensions:CGSizeMake(320, 50) alignment:UITextAlignmentCenter 
      fontName:@"Arial" fontSize:32.0] retain];
    _label.position = ccp(winSize.width/2, 
      winSize.height-(_label.contentSize.height/2));
    [self addChild:_label];
 
    // Standard method to create a button
    CCMenuItem *starMenuItem = [CCMenuItemImage 
      itemFromNormalImage:@"ButtonStar.png" selectedImage:@"ButtonStarSel.png" 
      target:self selector:@selector(starButtonTapped:)];
    starMenuItem.position = ccp(60, 60);
    CCMenu *starMenu = [CCMenu menuWithItems:starMenuItem, nil];
    starMenu.position = CGPointZero;
    [self addChild:starMenu];
 
  }
  return self;
}

First we create a label for debugging purposes. This should look pretty familiar – we did this in the last tutorial. However you’ll notice we’re using a new constructor this time that allows us to specify the dimensions of the label and the alignment. This way we specify the label size to be as wide as the window, and that the text should be centered. This is a handy technique to know, especially when you want to left or right justify text.

Next is the code where we create the button. We first create a menu item of class CCMenuItemImage and specify a selected and unselected image for the button. When we create the menu item, we specify a callback function to be called when the button is tapped (we’ll write this in a second). The last step is to create a menu to contain the button (or buttons).

Note that we create the menu at CGPointZero (a shortcut for 0,0). This is actually specifying where the center of the menu is. However, we also specify that the menu item is at offset (60, 60) relative to the center of the menu – so our button’s center is at (60, 60) on the screen.

Ok, one last bit of code to add. Underneath init add the callback function that will be called when the button is tapped:

- (void)starButtonTapped:(id)sender {
    [_label setString:@"Last button: *"];
}

Give it a compile and run, and if all goes well you should see the following:

Simple Button Screenshot

Toggle Buttons

Another common type of button you’ll need in an iPhone game is a toggle button. This is a button that has one image on it, until you tap it and it switches to another image. This could be used to toggle visibility of a control panel to make the best use of the limited screen real estate on the iPhone.

Luckily, Cocos2D comes with a special menu item class called CCMenuItemToggle that makes this easy. Let’s give this a shot! First add two more member variables to HelloWorldScene.h:

CCMenuItem *_plusItem; 
CCMenuItem *_minusItem;

And add the following cleanup code in your dealloc method:

[_plusItem release];
_plusItem = nil;
[_minusItem release];
_minusItem = nil;

Then add the following code in your init method right after you add the startMenu to the scene:

_plusItem = [[CCMenuItemImage itemFromNormalImage:@"ButtonPlus.png" 
  selectedImage:@"ButtonPlusSel.png" target:nil selector:nil] retain];
_minusItem = [[CCMenuItemImage itemFromNormalImage:@"ButtonMinus.png" 
  selectedImage:@"ButtonMinusSel.png" target:nil selector:nil] retain];
CCMenuItemToggle *toggleItem = [CCMenuItemToggle itemWithTarget:self 
  selector:@selector(plusMinusButtonTapped:) items:_plusItem, _minusItem, nil];
CCMenu *toggleMenu = [CCMenu menuWithItems:toggleItem, nil];
toggleMenu.position = ccp(60, 120);
[self addChild:toggleMenu];

First we create two CCMenuItemImages, just like we did in our previous example. Then comes the twist – we add both of those into a CCMenuItemToggle. This class keeps toggles between the elements inside, and keeps track of which is currently visible.

Note that we set the callbacks to nil when we created the CCMenuItemImages, but set it on the CCMenuItemToggle. This is to make it clear that any selectors on the CCMenuItemImages will not be called when they are inside a CCMenuItemToggle – only the CCMenuItemToggle’s selector will be called. Luckily, we can easily tell which of the menu items is visible in the callback.

Let’s see how by writing the callback! Add the following after your init method:

- (void)plusMinusButtonTapped:(id)sender {  
  CCMenuItemToggle *toggleItem = (CCMenuItemToggle *)sender;
  if (toggleItem.selectedItem == _plusItem) {
    [_label setString:@"Visible button: +"];    
  } else if (toggleItem.selectedItem == _minusItem) {
    [_label setString:@"Visible button: -"];
  }  
}

So as you can see, the CCMenuItemToggle has a selectedItem property that can show us which of the sub-items is currently visible (note it means visible, not which was tapped!)

So give it a compile and run, and if all goes well you should see the following:

Toggle Button Screenshot

Radio Buttons

A third common type of button that you might need in your iPhone projects are radio buttons. I found I needed some radio buttons for a game I was working on, but didn’t see an implementation for radio buttons in the Cocos2D source, so wrote an implementation of my own. While I was writing this article I came across two other guys who wrote implementations for radio button support in Cocos2D as well – so it looks like this will find its way into the Cocos2D source soon.

But it’s not in there yet, so in the meantime feel free to use the implementation I wrote or the ones I referenced above. For the purpose of this Cocos2D buttons tutorial, let’s implement radio buttons using the implementation I wrote. First, download CCRadioMenu.h and CCRadioMenu.m and drag them to the Classes directory of your project (making sure “Copy items into destination group’s folder (if needed)” is checked). Then add the following import to the top of HelloWorldScene.m:

#import "CCRadioMenu.h"

And the following to your init method after you add the toggle menu to the scene:

CCMenuItem *menuItem1 = [CCMenuItemImage itemFromNormalImage:@"Button1.png" 
  selectedImage:@"Button1Sel.png" target:self selector:@selector(button1Tapped:)];
CCMenuItem *menuItem2 = [CCMenuItemImage itemFromNormalImage:@"Button2.png" 
  selectedImage:@"Button2Sel.png" target:self selector:@selector(button2Tapped:)];
CCMenuItem *menuItem3 = [CCMenuItemImage itemFromNormalImage:@"Button3.png" 
  selectedImage:@"Button3Sel.png" target:self selector:@selector(button3Tapped:)];
CCRadioMenu *radioMenu = 
  [CCRadioMenu menuWithItems:menuItem1, menuItem2, menuItem3, nil];
radioMenu.position = ccp(120, 180);
[radioMenu alignItemsHorizontally];
radioMenu.selectedItem = menuItem1;
[menuItem1 selected];
[self addChild:radioMenu];

We create the CCMenuItemImages like usual, but instead of adding them to a CCMenu we add them to the new CCRadioMenu class. This class makes sure only one is selected at a time. We also set the first item to be selected by default at start.

The other new thing here is we call alignItemsHorizontally on the menu to take advantage of the neat auto-layout capability in Cocos2D. Note that the items will be laid out with respect to the center of the menu. Therefore, we can no longer center the menu at position (0, 0) – we have to move the center up and to the right a bit where we want the items to display.

One last thing to add – the callback methods as usual:

- (void)button1Tapped:(id)sender {
  [_label setString:@"Last button: 1"];
}
 
- (void)button2Tapped:(id)sender {
  [_label setString:@"Last button: 2"];
}
 
- (void)button3Tapped:(id)sender {
  [_label setString:@"Last button: 3"];
}

Once you compile and run this you should see something like the following:

Radio Buttons Creenshot

Behind The Scenes

If you look at how the menu system is implemented, you will note that menu items are CCNodes, but a Menu is a CCLayer. According to the Cocos2D best practices, you shouldn’t create a big hierarchy of layers, and should keep the count as low as you can.

So this means you probably should combine as many menu items into a single menu layer as you can. Also since a Menu derives from CCLayer you can’t make it run actions such as MoveTo, etc. I only mention this because I tried to move a whole menu of items when I was first starting and was wondering why it didn’t work :] Update: Eric from the comments below pointed out that CCLayer derives from CCNode, so you can run actions on it if you need to. Something else must have been going on when I was playing with it earlier, thanks Eric! :]

And That’s A Wrap!

Here’s a project with all of the code from the above tutorial.

Hope this was of use, and if you have any other cool tips about Buttons in Cocos2D please let me know!

Ray Wenderlich

Ray is an indie software developer currently focusing on iPhone and iPad development, and the administrator of this site. He’s the founder of a small iPhone development studio called Razeware, and is passionate both about making apps and teaching others the techniques to make them.

When Ray’s not programming, he’s probably playing video games, role playing games, or board games.

User Comments

38 Comments

[ 1 , 2 , 3 ]
  • Thanks for the wonderful tutorials about Cocos2d programming which I'm following recently. One question for the section "Toggle button" in this tutorial: Is the "retain" called when creating CCMenuItemImage necessarily?
    Bryant
  • Hi Ray,
    This is a really helpful tutorial. I am using radio buttons to create a questionnaire (part of my job) and I am cycling through a list of questions but I need to reset the selected button to unselected when moving to the next question. I have a "next" button as another CCMenu and want to reset the radio buttons on this. HOw can I do this?
    LynneJ777
  • Nice tutorial; somehow I cannot get the music to play; I did not import any extra framework, I think they are already there
    I do have the import statement:
    #import "SimpleAudioEngine.h"

    and here is the code in my init method (same as in the tutorial):

    [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"background-music-aac.caf"];
    // music is not playing when the app runs
    if( [[SimpleAudioEngine sharedEngine] isBackgroundMusicPlaying] )
    printf("Background music is playing\n");
    else
    printf("Background music is NOT playing\n");
    // output is: Background music is NOT playing

    The app is compiling fine and is running fine in the simulator, everything else is working
    And of course I did place the sound file in the resources folder

    Any idea why the music is not playing?
    quasquara
  • Here was the problem; I had dragged the sound files into the resources folder.
    I deleted them and added them to that same folder via File -> add Existing files ..
    The code is the same; the sound is now playing
    quasquara
  • Its great!
    SnowWolf
  • With IOS 6 and cocos2d 2.xthe previous code for CCRadioMenu does not work.

    [self itemForTouch: returns nil

    also selectedItem_ etc needs to be changed to _selectedItem etc.

    Ray: Are you planning an update.?
    edthenerd
  • edit: Nevermind, I guess I was doing something wrong, this works perfectly as intended.
    ModeSix
  • This does not work for cocos2d as of recent times. Just like edthenerd noted a year ago.
    it's me, you know
[ 1 , 2 , 3 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How to Make a Simple 2D Game with Metal.

Suggest a Tutorial - Past Results

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in October: Xcode 6 Tips and Tricks!

Sign Up - October

Our Books

Our Team

Tutorial Team

  • Dominik Hauser

... 52 total!

Update Team

... 14 total!

Editorial Team

... 22 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

... 4 total!