COCOS2D: ScrollView inside a CCScene blocks animation - iphone

I have a CCScene. In its right side I need to have a UIScrollView with some menu elements. I did it this way as I have explained in this previous question Cocos2d and UIScrollView
here is the method creating my scene
+(id) scene: (int) wld{
CCScene *scene = [CCScene node];
LevelsMenu *layer = [LevelsMenu node];
layer = [layer init:wld];
[scene addChild: layer];
[layer setScrollView:[LevelMenuControlView alloc]];
[[[CCDirector sharedDirector] openGLView] addSubview:layer.scrollView.view];
return scene;
}
notice that LevelMenuControlView is just an UIViewController implemented this way:
- (void)loadView{
LevelMenuView *scrollView = [[LevelMenuView alloc] initWithFrame:[UIScreen
mainScreen].applicationFrame];
scrollView.contentSize = CGSizeMake(862, 480);
scrollView.delegate = scrollView;
[scrollView setUserInteractionEnabled:TRUE];
[scrollView setScrollEnabled:TRUE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
[scrollView setShowsHorizontalScrollIndicator:FALSE];
self.view = scrollView;
[scrollView release];
}
While LevelMenuView is the UIScrollView containing the menu elements
It works quite fine. Now the problem is that in the left side of the scene I have a sprite animation that, If I do not touch the screen fine but as soon as I drag the scroll view up or down stops or goes at the same speed of my scrolling finger!!!
Any idea?

I have found this link where someone already experienced the same problem and posted a solution which actually works
http://www.cocos2d-iphone.org/forum/topic/11645
It basically consists in adding this code to the scroll view:
// This should go in your interface.
NSTimer *timer;
// Override
- (void)setContentOffset:(CGPoint)contentOffset {
// UIScrollView uses UITrackingRunLoopMode.
// NSLog([[NSRunLoop currentRunLoop] currentMode]);
// If we're dragging, mainLoop is going to freeze.
if (self.dragging && !self.decelerating) {
// Make sure we haven't already created our timer.
if (timer == nil) {
// Schedule a new UITrackingRunLoopModes timer, to fill in for
CCDirector while we drag.
timer = [NSTimer scheduledTimerWithTimeInterval:[[CCDirector sharedDirector] animationInterval] target:self selector:#selector(animateWhileDragging) userInfo:nil repeats:YES];
// This could also be NSRunLoopCommonModes
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopModes];
}
}
// If we're decelerating, mainLoop is going to stutter.
if (self.decelerating && !self.dragging) {
// Make sure we haven't already created our timer.
if (timer == nil) {
// Schedule a new UITrackingRunLoopMode timer, to fill in for CCDirector while we decellerate.
timer = [NSTimer scheduledTimerWithTimeInterval:[[CCDirector sharedDirector] animationInterval] target:self selector:#selector(animateWhileDecellerating) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
}
}
[super setContentOffset:contentOffset];
}
- (void)animateWhileDragging {
// Draw.
[[CCDirector sharedDirector] drawScene];
if (!self.dragging) {
// Don't need this timer anymore.
[timer invalidate];
timer = nil;
}
}
- (void)animateWhileDecellerating {
// Draw.
[[CCDirector sharedDirector] drawScene];
if (!self.decelerating) {
// Don't need this timer anymore.
[timer invalidate];
timer = nil;
}
}
Thanks a lot to these guys

Related

how to pause and resume an uiview animation when button clicking? [duplicate]

This question already has an answer here:
How to pause and resume UIView Animation (without Block Animation)?
(1 answer)
Closed 9 years ago.
i have scrollview, which has zoomscale property inside uiview begin animation,it animates well for some duration,but in between these animations i want to pause the animation and resume the animation,please help me out of this
[UIView beginAnimations:#"anim1" context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:fanim];
z = [[arrImage1 objectAtIndex:1] floatValue];
scroll.zoomScale = z;
NSLog(#" %f",scroll.zoomScale);
if (iw != 0 || ih != 0)
{
image1.frame = CGRectMake(0, 0, iw,ih);
}
z = [[arrImage2 objectAtIndex:1] floatValue];
scroll1.zoomScale = z;
z = [[arrImage3 objectAtIndex:1] floatValue];
scroll2.zoomScale = z;
[UIView commitAnimations];
That is possible by using nstimer. Try using nstimer for starting and pausing animation here is a set of code which you can use. Implement your animation within a method and use the nstimer to fire it and pause it:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self startTimer];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self stopTimer];
}
- (void)startTimer {
NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:3.0] interval:3.0 target:self selector:#selector(this is where your animation method name goest) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.animationmethodename = timer;
[timer release];
}
- (void)stopTimer {
[self.animationmethodname invalidate];
self.animationmethodtimer = nil;
}
Then replace the animationmethodname with the name of the method you are using to create the animation.

Stop animation on UIScrollview

I've set up some code to scroll (paging) the scrollview automatically, after a TimeInterval. Now I want to stop the scrollview animating, after 4 times. Could someone teach me how to do that?
This is my code.
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
- (void) onTimer {
// Updates the variable h, adding 320
abc += 320;
//This makes the scrollView scroll to the desired position
[UIView animateWithDuration:1.5f animations:^{
[scrollView setContentOffset:CGPointMake(abc, 0) animated:NO];
}];
}
First add an ivar to an NSTimer
{
NSTimer *timer;
}
timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
Then in the onTimer
- (void) onTimer {
//Create a static int
static int repetition = 0;
repetition ++;
if(repetition == 4)
{
repetition = 0;
//Stop the timer
[timer invalidate];
}
// Updates the variable h, adding 320
abc += 320;
//This makes the scrollView scroll to the desired position
[UIView animateWithDuration:1.5f animations:^{
[scrollView setContentOffset:CGPointMake(abc, 0)animated:NO];
}];
}

glowing effect of view disables button in it

I have uiscrollview with some buttons in it. I want to make one particular button to glow.
I made glowing-pulsating effect of button with following code:
(button in UIView)
- (IBAction)selectChampionAction:(id)sender{
if ( self.timer ) {
// Stop timer
[self.timer invalidate];
self.timer = nil;
// Workaround: we have to return the layer's delegate property to what it was
// Otherwise, we will encounter an unexpected result
self.viewForGlowing.layer.delegate = self.viewForGlowing;
} else {
// Woraround: UIView's layer property does not support animation by default
// assigning nil to layer's delegate property enables the animation somehow.
self.viewForGlowing.layer.delegate = nil;
// Start timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerExpired:) userInfo:nil repeats:YES];
[self.timer fire];
}
}
- (void) timerExpired:(NSTimer *) timer
{
if ( self.viewForGlowing.layer.shadowRadius == 20 ) {
// Increase
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:1.0f] forKey:kCATransactionAnimationDuration];
self.viewForGlowing.layer.shadowRadius = 30;
self.viewForGlowing.layer.shadowOpacity = 1;
[CATransaction commit];
} else {
// Decrease
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:1.0f] forKey:kCATransactionAnimationDuration];
self.viewForGlowing.layer.shadowRadius = 20;
self.viewForGlowing.layer.shadowOpacity = 0.7;
[CATransaction commit];
}
}
But when i press other buttons glowing button stops response.
I suppose that this happens because of this line of code
self.viewForGlowing.layer.delegate = nil;
My Questions:
Are there other approaches to make UIButton glow?
How to make button response after other buttons pressed?

NSInvocation Error

So I have been trying to create a menu for a game I have developed.
I am using Cocos2d and the game is set in portrait orientation. The menu.m file that I have written looks like this.
// Import the interfaces
#import "Menu.h"
#import "BankerInfo.h"
// HelloWorldLayer implementation
#implementation MenuLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
MenuLayer *layer = [MenuLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
// ask director the the window size
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *menubackground = [CCSprite spriteWithFile:#"Ninja Menu Background.png"];
menubackground.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:menubackground z:-1];
CCLabelTTF *title = [CCLabelTTF labelWithString:#"Catch It If You Can!" fontName:#"Zapfino" fontSize:22];
title.position = ccp(160, 420);
[self addChild: title];
CCLayer *menuLayer = [[CCLayer alloc] init];
[self addChild:menuLayer];
CCMenuItem *Bankerbutton = [CCMenuItemImage
itemFromNormalImage:#"Bankernotpressed.png"
selectedImage:#"Bankerpressed.png"
target:self
selector:#selector(startBanker:)];
//Bankerbutton.position = ccp(100, 175);
CCMenuItem *Babybutton = [CCMenuItemImage
itemFromNormalImage:#"Babynotpressed.png"
selectedImage:#"Babypressed.png"
target:self
selector:#selector(startBaby:)];
//Babybutton.position = ccp(100, 75);
CCMenuItem *Mommaduckbutton = [CCMenuItemImage
itemFromNormalImage:#"Mommaducknotpressed.png"
selectedImage:#"Mommaduckpressed.png"
target:self
selector:#selector(startMommaduck:)];
//Mommaduckbutton.position = ccp(350, 175);
CCMenuItem *Baseballbutton = [CCMenuItemImage
itemFromNormalImage:#"Baseballnotpressed.png"
selectedImage:#"Baseballpressed.png"
target:self
selector:#selector(startArcher:)];
//Baseballbutton.position = ccp(350, 75);
CCMenuItem *Newtonbutton = [CCMenuItemImage
itemFromNormalImage:#"Newtonnotpressed.png"
selectedImage:#"Newtonpressed.png"
target:self
selector:#selector(startArcher:)];
//Newtonbutton.position = ccp(350, 75);
CCMenu *menu = [CCMenu menuWithItems: Bankerbutton, Babybutton, Mommaduckbutton, Baseballbutton, Newtonbutton, nil];
menu.position = ccp(winSize.width/2, (winSize.height/2)-30);
[menu alignItemsInRows:
[NSNumber numberWithInt:2], [NSNumber numberWithInt:2], nil];
[menuLayer addChild: menu];
}
return self;
}
- (void) startBanker: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[BankerInfoLayer scene]];
}
/*- (void) startBaby: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Soldier scene]];
}
- (void) startMommaduck: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Mage scene]];
}
- (void) startBaseball: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Archer scene]];
}
- (void) startNewton: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Archer scene]];
}
*/
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
#end
And the error I'm getting looks like this.
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '+[NSInvocation invocationWithMethodSignature:]:
method signature argument cannot be nil'
Any Ideas?
The error states that thi is happening because of a bad call to an unexisting method signature. If you provide the functions used on the menu creation, does it still crash?
STEP 1: Make sure that the baseball button and newton button direct to the right selector, and not just the "startArcher:" that doesnt exist.
CCMenuItem *Baseballbutton = [CCMenuItemImage
itemFromNormalImage:#"Baseballnotpressed.png"
selectedImage:#"Baseballpressed.png"
target:self
selector:#selector(startBaseball:)]; // CHANGED HERE
//Baseballbutton.position = ccp(350, 75);
CCMenuItem *Newtonbutton = [CCMenuItemImage
itemFromNormalImage:#"Newtonnotpressed.png"
selectedImage:#"Newtonpressed.png"
target:self
selector:#selector(startNewton:)]; // CHANGED HERE
STEP 2: Uncomment the functions so you actually have valid selectors
- (void) startBanker: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[BankerInfoLayer scene]];
}
- (void) startBaby: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Soldier scene]];
}
- (void) startMommaduck: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Mage scene]];
}
- (void) startBaseball: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Archer scene]];
}
- (void) startNewton: (id) sender
{
[[CCDirector sharedDirector] replaceScene:[Backgroundinfo_Archer scene]];
}

Auto Scrolling UITextView Problem

I am trying to auto scroll my text view and reset it to the top once it's arrived at the end.
I use this code:
-(void)scrollTextView
{
CGPoint scrollPoint = stationInfo.contentOffset;
scrollPoint = CGPointMake(scrollPoint.x, scrollPoint.y + 2);
if (scrollPoint.y == originalPoint.y + 100)
{
NSLog(#"Reset it");
scrollPoint = CGPointMake(originalPoint.x, originalPoint.y);
[stationInfo setContentOffset:scrollPoint animated:YES];
[scroller invalidate];
scroller = nil;
scroller = [NSTimer
scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(scrollTextView)
userInfo:nil
repeats:YES];
}
else
{
[stationInfo setContentOffset:scrollPoint animated:YES];
}
}
As a result, the text view jumps around wildly, but I don't quite know why. Is there maybe a better way of detecting that the text view is at the bottom? Am I setting the the scrollPoint value wrong?
Edit:
ISSUE SOLVED! I stuck to NSTimer - the missing key was calling -display for the layer.
-(void)scrollTextView
{
//incrementing the original point to get movement
originalPoint = CGPointMake(0, originalPoint.y + 2);
//getting the bottom
CGPoint bottom = CGPointMake(0, [stationInfo contentSize].height);
//comparing the two to detect a reset
if (CGPointEqualToPoint(originalPoint,bottom) == YES)
{
NSLog(#"Reset");
//killing the timer
[scroller invalidate];
scroller == nil;
//setting the reset point
CGPoint resetPoint = CGPointMake(0, 0);
//reset original point
originalPoint = CGPointMake(0, 0);
//reset the view.
[stationInfo setContentOffset:resetPoint animated:YES];
//force display
[stationInfo.layer display];
scroller = [NSTimer
scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(scrollTextView)
userInfo:nil
repeats:YES];
}
else
{
[stationInfo setContentOffset:originalPoint animated:YES];
}
}
You could also use CoreAnimation and animate the bounds property directly. First animate the scrolling, then in the delegate callback that the animation has finished you reset the content offset.
The callback method has to have the signature
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
You could also use the new block-based methods if you are targeting iOS 4.0 and above. Then there are two blocks to be passed: in the first one you specify what to animate and in the second what to do when the animation is over.
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Your problem than collapses into a single line of code:
[stationInfo animateWithDuration:10.f delay:0.f options:0 animations:^{
[stationInfo setContentOffset:CGPointMake(0, [stationInfo contentSize].height)];
} completion:^(BOOL finished){
if (finished) [stationInfo setContentOffset:CGPointMake(0,0)];
}];
To be honest I am not 100% sure about the precise block syntax but this is how it should work.