Scrolling Menus in Cocos2d

Posted: December 30th, 2010 | Author: | Filed under: Cocos2d, Objective-c, Tutorials | 30 Comments »

EDIT:
Please note I am no longer managing this code. It has moved here: https://github.com/cocos2d/cocos2d-iphone-extensions/tree/master/Extensions/CCScrollLayer

I must have spent over a week coming up with a good solution for this. The effect I was after was a scrollable menu for my game, similar to Angry Birds where the user can flip between levels but also see a preview of the previous/next page on either end of the screen. I came across Nate Murray’s excellent UIScrollView solution but it wasn’t quite what I was looking for and using UIScrollView seemed like too much of an overhead. I needed something simple, preferably wrapped up in a single class.

After searching various community forums I finally came across DK101′s solution which was exactly what I was looking for. A simple CCLayer subclass that would allow me to create an array of CCLayers then add whatever I wanted to it then add the whole lot to my scene. Simple. You are able to add anything you can add to a CCLayer – so a label, menu, image etc. It will all work nicely.

I ended up making a few modifications to it like updating it to work with Cocos2d 0.99.5 and adding the option to set the width of the screens for the Angry Birds style preview effect. There was also an issue with the touch delegate on subsequent scenes. I have put my changes up on Github. Here’s a sample:

To use it in your project:

1. add both files to your project
2. in your scene import CCScrollLayer.h
3. in your scene’s init method construct each layer and pass it to the CCScrollLayer class:

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// get screen size
CGSize screenSize = [CCDirector sharedDirector].winSize;
 
/////////////////////////////////////////////////
// PAGE 1
////////////////////////////////////////////////
// create a blank layer for page 1
CCLayer *pageOne = [[CCLayer alloc] init];
 
// create a label for page 1
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Page 1" fontName:@"Arial Rounded MT Bold" fontSize:44];
label.position =  ccp( screenSize.width /2 , screenSize.height/2 );
 
// add label to page 1 layer
[pageOne addChild:label];
 
/////////////////////////////////////////////////
// PAGE 2
////////////////////////////////////////////////
// create a blank layer for page 2
CCLayer *pageTwo = [[CCLayer alloc] init];
 
// create a custom font menu for page 2
CCLabelBMFont *tlabel = [CCLabelBMFont labelWithString:@"Page 2" fntFile:@"customfont.fnt"];
CCMenuItemLabel *titem = [CCMenuItemLabel itemWithLabel:tlabel target:self selector:@selector(testCallback:)];
CCMenu *menu = [CCMenu menuWithItems: titem, nil];
menu.position = ccp(screenSize.width/2, screenSize.height/2);
 
// add menu to page 2
[pageTwo addChild:menu];
////////////////////////////////////////////////
 
// now create the scroller and pass-in the pages (set widthOffset to 0 for fullscreen pages)
CCScrollLayer *scroller = [[CCScrollLayer alloc] initWithLayers:[NSMutableArray arrayWithObjects: pageOne,pageTwo,pageThree,nil] widthOffset: 230];
 
// finally add the scroller to your scene
[self addChild:scroller];

Download the source

And a MASSIVE thank you to DK101

30 Comments »
  • http://dk101.net Simon Skinner

    Very nice implementation, I had started messing around with changing the size of the layers but got distracted with Christmas. However i’m glad you got good use from my code :D

  • http://winduprocketapps.com Matt Welch

    Any thoughts on how I might make the menu items themselves swipable as well? In other words, I’d like the menu items to trigger when they’re touched, but to basically ignore the touch if it’s actually a swipe, and pass the touch/swipe to the layer, so that I can get my nice scrolling.

    As it is now, I have to swipe above or below my menu item to scroll. Trying to swipe on the menu item itself is basically ignored.

    To use your Angry Birds example, touching one of those menu items, and then commencing with a swipe will scroll the menu.

    Thanks to you and DK101 for the great code. Really a nice solution.

  • Giv

    Hi Matt,

    I see what you mean. Let me have a think about it and get back to you :)

  • http://Brandonreynolds.com Brandon Reynolds

    Hi! I have developed something very similar to your scroll view but wraps CCMenuItems into a scrollable grid like Angry Birds.

    Please take a look and let me know what you think!

    http://brandonreynolds.com/blog/2011/01/09/cocos2d-sliding-menu-grid/

  • Giv

    Thanks for this Brandon. It looks great!

  • http://Website Shuwee

    Nice post, thanks a lot!!

  • Lukas182

    Thanks for this such nice code! :)

  • Zero07

    Great stuff, it works perfect!!! read the instructions and got it working

  • Anonymous

    You’re very welcome but DK101 did all the hard work :)

  • Lukas182

    Hey,

    How could I do to scale the menu of the page when Im on the currentpage, and make it scale back to its size when I change the page?

    Thanks,

    Dave.

  • Anonymous

    Hi Dave,

    Do you mean when you flip to a menu page you want the screen to change scale, then when you flip to another page have it scale back to normal?

    Since the page flips are just CCActions, you could change this to a sequenced action so after it eases to the current page, it then runs the next action which is CCScaleTo.

    To make it go back to normal scale you could add a new “CCCallFuncN” action to the start of the action sequence that first checks to see if the scale of the current page is 1.0. If it is more than 1.0 then you know you need to scale it back down first before easing.

    I hope that helps you get started.

    G

  • Anonymous

    Hi Dave,

    Do you mean when you flip to a menu page you want the screen to change scale, then when you flip to another page have it scale back to normal?

    Since the page flips are just CCActions, you could change this to a sequenced action so after it eases to the current page, it then runs the next action which is CCScaleTo.

    To make it go back to normal scale you could add a new “CCCallFuncN” action to the start of the action sequence that first checks to see if the scale of the current page is 1.0. If it is more than 1.0 then you know you need to scale it back down first before easing.

    I hope that helps you get started.

    G

  • Lukas182

    Hey giv,

    Thanks for the answer! Ye I meant, that.. Scale the page ( or the menus/images) when its your current page, and scale it back when you move to next or previous. Ill try it with your idea! :D

    You find out any way to do what @Matt Welch said on his comment? I mean, ignore the menu item touches when you do a swipe? That would be very nice ;) .

    Thanks again.

    Dave.

  • Anonymous

    Not yet, sorry. Been quite busy but will investigate soon :)

  • http://liort.wordpress.com leo

    Great Job, thanks.
    I’ve ported this into c++ for cocos2d-x project and it works great.
    http://www.cocos2d-x.org/boards/6/topics/1090

  • Anonymous

    Nice work Leo!

  • Glenn

    I’m not sure what Matt means by “menu items themselves swipeable”, but I also can’t get any of the child nodes in the CCScrollLayer to receive touches. Which makes sense given that the CCScrollLayer is returning YES to ccTouchBegan, so no other nodes will ever get any events for that touch (move, end, etc). So, how in your example movie does the Stage 2 element ever detect the touch that it has been pressed (as opposed to the CCScrollLayer claiming the touch for it’s own swipe action)?

  • mpf

    thank you so much for this!

  • juanvi 1b

    hi! really nice code, thanks for posting.
    is there a way of getting it to go vertically or get only half of the screen?

    thanks a lot

  • Anonymous

    No problem.

    Shouldn’t be too hard to make it scroll vertically. Try playing around with the scroll height and the Y position.

  • xozyain

    does we need

    [pageOne release];

    after [pageOne addChild:label]; ?

  • http://twitter.com/parkourDev Stepan Generalov

    Hello! I’ve improved CCScrollLayer a bit: https://github.com/givp/Cocos2d-Menu-Scroller/pull/1
    I’ve added MIT License, to make it possible add CCScrollLayer as a part of official cocos2d-iphone-extensions repo.
    So i need your formal acquiescence.
    Thanks!

  • Giv

    Excellent! Very nice work.

  • Anonymous

    Thank you, I’ve merged your changes.

  • leo

    I solved this issue in my c++ implemention, I’ve added special class for scrollable buttons, see post here:
    http://www.cocos2d-x.org/boards/6/topics/1090?r=2059#message-2059

  • John

    Awesome. Just integrated this into my project with no trouble. Thanks so much for sharing this!!

  • Iphoneguy12

    Is anyone else having a problem getting this to work? When I build it I just get this error:

    CCScrollLayer.m:179: error: ‘touch’ undeclared (first use in this function)

  • Agustin Bereciartua

    Hi can you post any sample code? pleasse
    Ihave a lot of problem with scroll layer
    thankksss from argentina

  • Anonymous

    Hi Agustin, there’s a sample code above. I’m not really maintaining this anymore. Please have a look at Stepan’s improvements on Github.

  • Bin

    very nice, thank you.