Scrolling Menus in Cocos2d

Posted: December 30th, 2010 | Author: | Filed under: Cocos2d, Objective-c, Tutorials | 36 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

36 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 :)

      • 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)?

  • 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

      • 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 :)

    • 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

  • 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!

  • 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.

  • 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.

  • Ntchungbk

    thanks you very much

  • Boss

    It does not work. The error pops un on a linking stage

  • http://www.facebook.com/profile.php?id=627203841 Emil Erlandsson

    This was a great help, thank you!

  • CSuiter

    Hi, this code works brilliantly, but is there any easy way to make the scroll vertical? Thanks.

  • Pingback: 如何在iPhone的Cocos2d中使用滚动视图? - iOS问答 - 开发者问答

  • Pingback: [Cocos2D-X] 分享一个类似UIScrollView的Cocos2d类实现 - 移动端游戏开发 - 开发者问答