Creating a simple Simon clone: How to wait between updates? - iphone

I'm learning Cocoa/Objective-C/iPhone SDK, and as a simple project to apply what I've learned, I wanted to create a simple version of the Simon game of old. Four colored buttons, you're shown a sequence (Red, Green, Blue, Red, etc.) and you have to repeat the sequence back.
I believe I have most of it figured out, save one piece: showing the sequence to the user. Specifically, how to implement the delay between highlighting the button & setting it back to normal after 200ms.
If I sleep in the main run loop, the update doesn't happen properly (even if I explicitly call setNeedsDisplay). If I spawn off a new thread, things get complicated quickly as my class method needs to refer back to UI elements (instant variables).
Any advice?

You can use the +setAnimationStartDate: method on UIView to set up some animations to do this. To ensure that no one presses your buttons during the animation, call -[UIApplication beginIgnoringInteractionEvents] at the start of your animations and -[UIApplication endIgnoringInteractionEvents] at the end.
A written-in-the-text-field example of what I’m talking about:
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
button1.highlighted = YES;
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationStartDate: CFAbsoluteTimeGetCurrent() + 0.2];
[UIView setAnimationsEnabled:NO];
button1.highlighted = NO;
[UIView commitAnimations];
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationStartDate: CFAbsoluteTimeGetCurrent() + 0.25];
[UIView setAnimationsEnabled:NO];
button2.highlighted = YES;
[UIView commitAnimations];
// etc.
[[UIApplication sharedApplication] performSelector:#selector(endIgnoringInteractionEvents) withObject:nil afterDelay:yourTotalDelay];

"simon" is a very simple MIDI sequencer. therefore I would highly suggest looking into the CoreAudio Framework, in particular the real-time-clock functionality.

gerry3 gives an answer here:
How can I show a sequence of buttons highlighting?

Read the docs for the Core Animation framework.

Related

Expand and collapse animation not working in ios6

I am working on UIView. This view is expand and collapse on button click. Its working on iOS<=5 but not working iOS6. Can you please correct my code or explain it.
Thanks.
My Code:
CGFloat x= 60,y = 391,w1=210,h1=48;
[ViewShare setHidden:YES];
[UIView beginAnimations:#"animationOff" context:NULL];
[UIView setAnimationDuration:0.2f];
[ViewShare setFrame:CGRectMake(x, y, w1, h1)];
[UIView commitAnimations];
And Ref Page
Thanks..
Mani, you should use the block based animation instead, because the use of commit animation is discouraged after iOS 4.0.
There is lot of GUI bug code if you try to cross lot of IOS, and try to use "deprecated" method. By the way your code is correct.
If you don't use IOS before 4.0, use instead
animateWithDuration:animations:completion:
Here's an example:
[UIView animateWithDuration: 0.2f
animations:^{
[ViewShare setFrame:CGRectMake(60, 391, 210, 48)];
}
completion:^(BOOL finished){
// what you want when the animation is done
}];
Note that sometime, you need to switch behavior depending on iOS version, it is very always GUI related. That make me very nervous. :)

Animation is effecting every view even after calling commitAnimation

I am developing a Cocos2D game. On a stage a show a UIView giving users a list of tasks to perform. There are text fields which I move up or down using Animation. My code is below.
[textField setFrame:CGRectMake(textField.frame.origin.x, textField.frame.origin.y, textField.frame.size.width, textField.frame.size.height)];
[inviteButton setFrame:CGRectMake(inviteButton.frame.origin.x, inviteButton.frame.origin.y, inviteButton.frame.size.width, inviteButton.frame.size.height)];
[headingLabel setFrame:CGRectMake(headingLabel.frame.origin.x, headingLabel.frame.origin.y, headingLabel.frame.size.width, headingLabel.frame.size.height)];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDelegate:self];
[textField setFrame:CGRectMake(textField.frame.origin.x, textField.frame.origin.y-50, textField.frame.size.width, textField.frame.size.height)];
[inviteButton setFrame:CGRectMake(inviteButton.frame.origin.x, inviteButton.frame.origin.y-50, inviteButton.frame.size.width, inviteButton.frame.size.height)];
[headingLabel setFrame:CGRectMake(headingLabel.frame.origin.x, headingLabel.frame.origin.y-30, headingLabel.frame.size.width, headingLabel.frame.size.height)];
[UIView commitAnimations];
[self.view.layer removeAllAnimations];
Problem is that even after user is done and he presses the cancel button to remove this view from the super view, every view including UIAlertView and other subviews appearing in my map after removing that view are effected by the animation i used to move text fields and label up or down. UIAlertView is using same Animation to show up and so is happening other subviews. Can anyone please tell me why this is happening?
Best Regards
You have to set the animationID to a unique value in the beginAnimations:context: method.
See the official doc
Also this doc states:
Use of this method is discouraged in iOS 4.0 and later. You should use the block-based animation methods to specify your animations instead.

How do you code nested UIView animations?

I need to perform 9 animations where each animation starts after previous one. So I have huge gigantic piece of code with UIView animateWithDuration calls. It really looks ugly :) Is there any better options than making 8 additional methods and nesting them?
Core Animation is your friend.
http://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html
Or you can write all of these 9 steps into one method by using block-based animation method. But it still dosen't look beautiful and iOS 4 is requied.
You can still use the UIView animation or coreanimation and use the delegate call back i.e:
[UIView beginAnimations:#"fadeIn" context:nil];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDidStopSelector:#selector(finishAnimation:finished:context:)];
[UIView setAnimationDelegate:self];
theview.alpha = 1;
[UIView commitAnimations];
then in the finishAnimation method you can call another animation.

Duration when using CGAffineTransformTranslate

I am moving a view using CGAffineTransformTranslate and want to slow the move down. I tried using [UIView setAnimationDuration] but it does not do anything and the docs discourages it's use in iOS 4.0 and later.
whatIfToolBar.transform = CGAffineTransformTranslate(whatIfToolBar.transform,0.0, -whatIfToolBar.frame.size.height);
What is the proper way to set the duration?
Thanks,
John
I should have read further before asking my question...
[UIView setAnimationDuration] only works when using Begin/Commit methods and must be called between calls to begin and commit animations and before changing any animatable properties of the view.
For iOS 4 or later applications you should use block-based methods for animation. Duration is set when calling a block method. See the Animations section of the View Programming Guide for iOS".
If you application will be run in iOS 3.2 and earlier you must use Begin/Commit methods.
In my case I used Begin/Commit methods...
[UIView beginAnimations:#"whatIfToolBar" context:whatIfToolBar];
[UIView setAnimationDuration:0.5];
whatIfToolBar.transform = CGAffineTransformTranslate(CGAffineTransformIdentity,0.0, - whatIfToolBar.frame.size.height);
[UIView commitAnimations];
If I were to use block-based methods it would look like this...
[UIView animateWithDuration:0.5
animations:^{
whatIfToolBar.transform = CGAffineTransformTranslate(CGAffineTransformIdentity,0.0, -whatIfToolBar.frame.size.height);
}
];
John

Troubles with UIView, animateWithDuration and beginFromCurrentState

I have a custom view that has to track the user's location. I put the following code in touchesBegan, as well as in touchesMoved:
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
cursorView.center = locationOfTouch;
} completion:^(BOOL finished){}];
It seems fairly straightforward to me. I'd expect the view to always animate to the user's current location, even if that location is changed and the view is still animating (because of the beginFromCurrentState option).
Yet, each animation finishes completely. They don't 'transition' to the new animation, no they finish first, then they start the new animation.
I tried adding this line in touchesMoved:
[cursorView.layer removeAllAnimations];
Doesn't do anything. No animation is cancelled. Any ideas?
with [cursorView.layer removeAllAnimations]; you access the layer of the view but you have set the animation not to the layer but the view directly.
Try:
[UIView beginAnimations:nil context:NULL]
[UIView setAnimationDuration:0.2];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
cursorView.center = locationOfTouch;
[UIView commitAnimations];
and leave out the removeAllAnimations and it should transition from current state.
Looks like this is a derivative of another question you asked that I just answered, but the same idea applies.
You don't need to use blocks to do this. Just wrap your setCenter in a UIView animation. Like this:
[UIView beginAnimations:nil context:NULL]
[UIView setAnimationDuration:0.2f];
cursorView.center = locationOfTouch;
[UIView commitAnimations];
Keep in mind that 0.2 seconds is pretty fast. It may appear to be "jumping" to the position. To confirm, set the duration to something like 2.0 seconds and see if it's animating.
Best regards.
Technically, the view should automatically animate with a default time of 0.25 seconds. That is what the manuals say, however, I'm currently on here because the manuals don't seem to be very accurate regarding animation.
Also, the beginAnimations call is being phased out, you might want to use a CATransaction instead