Trying to get 2 animations to work simultaneously - objective-c

In the next code there are 2 methods that are called one after the other. The first one makes the center button disappear and the second one makes the tabbar disappear. Separately they are working fine.
My problem is that when I try to call them one after the other hideCenterButton doesn't animate. Instead of rolling to the left of the screen the button just disappears.
-(void)hideCenterButton:(BOOL)animated
{
if(animated){
[UIView animateWithDuration:0.3
delay:0.0f
options:UIViewAnimationCurveLinear
animations:^{
CGRect frame = self.centerButton.frame;
frame.origin.x = -100;
self.centerButton.frame = frame;
}
completion:^(BOOL finished){
}];
}}
...
- (void)hideTabBar:(UITabBarController *) tabbarcontroller
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
//[UIView setAnimationDelay:1];
for(UIView *view in tabbarcontroller.view.subviews)
{
if([view isKindOfClass:[UITabBar class]] || [view isKindOfClass:[UIImageView class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 480, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 480)];
}
}
[UIView commitAnimations];
}

You have probably hit into some kind of limitation of UIView-based animations. Several things you might try are:
use animateWithDuration also for hideTabBar:: indeed, beginAnimation is the old-way of animating UIView; animateWithDuration is more recent (introduced with iOS4); by using the same animation calls in bot cases you might get better results; this should be straightforward; this should work:
- (void)hideTabBar:(UITabBarController *) tabbarcontroller
{
[UIView animateWithDuration:0.3
delay:0.0f
options:UIViewAnimationCurveLinear
animations:^{
for(UIView *view in tabbarcontroller.view.subviews)
{
if([view isKindOfClass:[UITabBar class]] || [view isKindOfClass:[UIImageView class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 480, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 480)];
}
}
}
completion:nil];
}
rewrite your animation using Core Animation: this is a slightly lower-level mechanism but it allows you to get the best results in terms of performance; this is, e.g., how you could rewrite the first animation:
first you need to provide a callback:
- (void)animationDidStop:(CABasicAnimation*)anim finished:(BOOL)flag {
CALayer* layer = [anim valueForKey:#"animationLayer"];
layer.position = [anim.toValue CGPointValue];
}
then you can animate your button like this:
CABasicAnimation* move = [CABasicAnimation animationWithKeyPath:#"position"];
CGPoint pos = self.centerButton.center;
pos.x -= 100;
move.toValue = [NSValue valueWithCGPoint:pos];
move.duration = 0.3;
move.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
move.removedOnCompletion = NO;
move.fillMode = kCAFillModeForwards;
move.autoreverses = NO;
move.delegate = self;
[move setValue:self.centerButton.layer forKey:#"animationLayer"];
[self.centerButton.layer addAnimation:move forKey:#"buttonPosition"];
Using Core Animation, you will end up writing more code and you will need to allow some time to learn Core Animation basics, but that was the only way I could solve a similar issue I had with two related animation.
Hope it helps.

Related

Keep the End Position of Animation

I tried everything, but everytime its the same problem:
i want to move a button for y = 276. when the animation ends, my buttons jumps back to the startposition, but i want that the endanimationposition is the new position, and the button has to stay there, until a new animation is called.
tried with
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
CGPoint p = _cell00.center;
p.x += 100;
_cell00.center = p;
[UIView commitAnimations];
[UIView beginAnimations:nil context:nil];
same issue like
[UIView animateWithDuration:1.0 animations:^{
_cell00.frame = CGRectOffset(_cell00.frame, 0, 20);
}];
or
[UIView animateWithDuration:0.5 animations:^{
_cell00.center = CGPointMake(0, 20.0);
}];
EDIT:
I call this Animation with my Button called statisticsBUttonPressed.
- (IBAction)statisticsButtonPressed:(id)sender {
if (statOrGame == 0) {
statOrGame = 1;
}else {
statOrGame = 0;
}
NSLog(#"statOrGame? %d", statOrGame);
[self animateView:(UIButton *)sender];
}
-(void) animateView:(UIButton *)sender {
sender.userInteractionEnabled = NO;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{ // The iOS device = iPhone or iPod Touch
NSLog(#"IS IPHONE 5");
CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
if (iOSDeviceScreenSize.height == 568)
{
if (statOrGame) {
[UIView animateWithDuration:1.0 animations:^{
_cell00.frame = CGRectOffset(_cell00.frame, 0, 20);
}];
}
}
}
}
The problem is that you're using auto layout. You can't (just) change the frame of a view that is positioned by auto layout, because what positions the view is its constraints. You need to change the constraints. You can do this at the end of the animation, or you can simply animate the change of constraints.

cannot move frame inside method inside textfielddidbeginediting

I am trying to move textfield frame when you begin editing; hacked it by putting method call in "keyboardWasShown". But that is delayed suboptimal solution. How can I move textfield around from method inside of didBeginEditing? The method gets called but it just seems to ignore moving the frame around. Interestingly, the frame moves correctly the second time I interact with the commentField. Code:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if ([textField isEqual:self.commentField]) {
self.commentField.text=#"";
[self.commentField setTextColor:[UIColor blackColor]];
[self animateTextFieldUp:self.commentField];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextFieldDown:self.commentField];
}
CGFloat KEYBOARD_HEIGHT;
- (void)keyboardWasShown:(NSNotification*)notification {
// Get the size of the keyboard.
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
KEYBOARD_HEIGHT=keyboardSize.height;
[self animateTextFieldUp:self.commentField];
}
-(void)animateTextFieldUp:(UITextField*)textField
{
int movement = movementDistance;
[UIView beginAnimations: #"animateTextField" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.commentField.frame = CGRectOffset(self.commentField.frame, 0, movement);
self.bottomView.frame = CGRectOffset(self.bottomView.frame, 0, movement);
self.sendButton.frame = CGRectOffset(self.sendButton.frame, 0, movement);
[UIView commitAnimations];
}

Interactive UIViewController Transition with new "usingSpringWithDamping:initialSpringVelocity"-animation

I try to implement a interactive custom modal UIViewController transition using the new iOS7 APIs. Everything is working as desired as long as I don't use the new
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
method for animating the transition. It is working fine if I invoke the transition via a button. The problem occurs when the transition is interactive, driven by a GestureRecognzier. If I release my finger the animation is immediately cut of and won't finish. The same code is working if I use
animateWithDuration:delay:options:animations:completion:
instead.
Here is the code which animates the transition:
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect fullFrame = transitionContext.containerView.frame;
if (!self.dismissal) {
[transitionContext.containerView addSubview:fromViewController.view];
[transitionContext.containerView addSubview:toViewController.view];
fromViewController.view.frame = fullFrame;
toViewController.view.frame = CGRectMake(0, fullFrame.size.height, 0, 0);
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0.5 options:UIViewAnimationOptionTransitionNone animations:^{
//animation
toViewController.view.frame = CGRectMake(0,0, fullFrame.size.width, fullFrame.size.height);
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
} else {
[transitionContext.containerView addSubview:toViewController.view];
[transitionContext.containerView addSubview:fromViewController.view];
fromViewController.view.frame = fullFrame;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:.5 initialSpringVelocity:.5 options:0 animations:^{
fromViewController.view.frame = CGRectMake(0, fullFrame.size.height, fullFrame.size.width, fullFrame.size.height);
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
//This is working fine
// [UIView animateWithDuration:[self transitionDuration:transitionContext]/2 animations:^{
// fromViewController.view.frame = CGRectMake(0, fullFrame.size.height, fullFrame.size.width, fullFrame.size.height);
// } completion:^(BOOL finished) {
// [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
// }];
}
This is the code behind the gesture recogniser:
#pragma mark - Gesture recognition
- (void)handlePan:(UIScreenEdgePanGestureRecognizer *)pgr
{
CGPoint translation = [pgr translationInView:pgr.view];
CGFloat percentage = fabs(translation.x / CGRectGetWidth(pgr.view.frame));
switch (pgr.state) {
case UIGestureRecognizerStateBegan:
self.interactionInProgress = YES;
[self.respondingVC proceedToNextViewController];
break;
case UIGestureRecognizerStateChanged: {
[self updateInteractiveTransition:percentage];
break;
}
case UIGestureRecognizerStateEnded:
if(percentage < 0.5) {
[self cancelInteractiveTransition];
} else {
[self finishInteractiveTransition];
}
self.interactionInProgress = NO;
break;
case UIGestureRecognizerStateCancelled:
[self cancelInteractiveTransition];
self.interactionInProgress = NO;
default:
break;
}
}
It looks like you're using UIPercentDrivenInteractiveTransition. When you call finishInteractiveTransition, UIPercentDriventInteractiveTransition does not call animateTransition.
You should override both updateInteractiveTransition and finishInteractiveTransition to update your view states.
All my attempts to begin an animation block inside an interactive transition have ended in failure. I finally at least got it to finish the animation by calling [self updateInteractiveTransition:1] in finishInteractiveTransition. Otherwise the views will be left in the state they were in and the transition never completes or the transition is treated as completed but your views are in strange places.

Hide UINavBar and UITabBar at the same time with animation

Edit: I awarded the bounty to john since he put a lot of effort into his answer, and would get it anyways, but there's still no working solution. I am still looking for an answer, if someone knows how to do this it'd be greatly appreciated.
I want to add a "maximize" button to my app that hides the navigation and tab bar. The navbar and tabbar should slide in/out smoothly, and the inner/content view should also expand and shrink at the same rate as the navbar and tabbar.
I used [self.navigationController setNavigationBarHidden: YES/NO animated: YES]; for the navbar and found this thread How to hide uitabbarcontroller for hiding the tabbar.
UITabBar class extension:
- (void) setTabBarHidden:(BOOL)hidden animated:(BOOL)animated {
CGRect screenRect = [[UIScreen mainScreen] bounds];
float screenHeight = screenRect.size.height;
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
screenHeight = screenRect.size.width;
}
if (!hidden) {
screenHeight -= self.tabBar.frame.size.height;
}
[UIView animateWithDuration: (animated ? UINavigationControllerHideShowBarDuration : 0) animations: ^{
for (UIView* each in self.view.subviews) {
if (each == self.tabBar) {
[each setFrame: CGRectMake(each.frame.origin.x, screenHeight, each.frame.size.width, each.frame.size.height)];
} else {
[each setFrame: CGRectMake(each.frame.origin.x, each.frame.origin.y, each.frame.size.width, screenHeight)];
}
}
} completion: ^(BOOL finished) {
NSLog(#"Animation finished %d", finished);
}];
}
The problem is when I use the two at the same time (hiding/showing the nav and tab bar), it's not clean. If the navbar comes first, anything anchored to the bottom jumps (see example below), and if the tabbar comes first, the top jumps.
Example: I position the UIButton in the bottom right and set its autoresizing mask
resizeButton.frame = CGRectMake(self.view.bounds.size.width - 50, self.view.bounds.size.height - 100, 32, 32); // hardcoded just for testing purposes
resizeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
But when the navbar and tabbar are minimized the UIButton jumps between the two states (doesn't slide along with the tab bar). However, if I change it to attach to the top right, it slides perfectly with the nav bar.
Does anyone know how to solve this?
Edit:
This is the closet and most elegant solution I have so far (just trying to get a working concept):
[UIView animateWithDuration: UINavigationControllerHideShowBarDuration animations: ^{
if (self.isMaximized) {
self.tabBarController.view.frame = CGRectMake(0, 20, screenRect.size.width, screenRect.size.height + 49 - 20);
[self.navigationController setNavigationBarHidden:YES animated:YES];
} else {
self.tabBarController.view.frame = CGRectMake(0, 20, screenRect.size.width, screenRect.size.height - 20);
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
} completion: ^(BOOL finished) {
NSLog(#"Frame done: %#", NSStringFromCGRect(self.view.frame));
return;
}];
On maximizing:
Slides the navbar up, and slides the tabbar down, at the same time
The top of the inner/content view slides up, and the bottom of this view jumps down
On minimizing:
Slides the navbar down, and slides the tabbar up, at the same time
The top of the inner/content view slides down properly, but the bottom jumps to the final value, leaving whitespace which is then covered by the sliding tabbar
If I rearange the order of the minimizing-animations (so the navbar animatino is called first), then the top in the inner/content view jumps
the solution i use should eliminate the jump problem you see.
this solution is derived from an Objective-C category found Carlos Oliva's github page, and while the copyright in that code is "all rights reserved", i wrote him and he provided permission for use.
my category code varies only slightly from his code. also, find below the category code the invocation code that i use in my app.
from UITabBarController+HideTabBar.m
// the self.view.frame.size.height can't be used directly in isTabBarHidden or
// in setTabBarHidden:animated: because the value may be the rect with a transform.
//
// further, an attempt to use CGSizeApplyAffineTransform() doesn't work because the
// value can produce a negative height.
// cf. http://lists.apple.com/archives/quartz-dev/2007/Aug/msg00047.html
//
// the crux is that CGRects are normalized, CGSizes are not.
- (BOOL)isTabBarHidden {
CGRect viewFrame = CGRectApplyAffineTransform(self.view.frame, self.view.transform);
CGRect tabBarFrame = self.tabBar.frame;
return tabBarFrame.origin.y >= viewFrame.size.height;
}
- (void)setTabBarHidden:(BOOL)hidden {
[self setTabBarHidden:hidden animated:NO];
}
- (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated {
BOOL isHidden = self.tabBarHidden;
if (hidden == isHidden)
return;
UIView* transitionView = [self.view.subviews objectAtIndex:0];
if (!transitionView)
{
#if DEBUG
NSLog(#"could not get the container view!");
#endif
return;
}
CGRect viewFrame = CGRectApplyAffineTransform(self.view.frame, self.view.transform);
CGRect tabBarFrame = self.tabBar.frame;
CGRect containerFrame = transitionView.frame;
tabBarFrame.origin.y = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
containerFrame.size.height = viewFrame.size.height - (hidden ? 0 : tabBarFrame.size.height);
[UIView animateWithDuration:kAnimationDuration
animations:^{
self.tabBar.frame = tabBarFrame;
transitionView.frame = containerFrame;
}
];
}
from my ScrollableDetailImageViewController.m
- (void)setBarsHidden:(BOOL)hidden animated:(BOOL)animated
{
[self setTabBarHidden:hidden animated:animated];
[self setStatusBarHidden:hidden animated:animated];
// must be performed after hiding/showing of statusBar
[self.navigationController setNavigationBarHidden:hidden animated:animated];
}
- (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated
{
id parent = self.navigationController.parentViewController;
if ([parent respondsToSelector:#selector(isTabBarHidden)]
&& hidden != [parent isTabBarHidden]
&& [parent respondsToSelector:#selector(setTabBarHidden:animated:)])
[parent setTabBarHidden:hidden animated:animated];
}
Just try this code, If it is working. I have written this code a year before. But, still it works good for me.
I didn't used the block based animations. Because it was written when I am new to iOS. Just try and optimize yourself as you wanted.
- (void) hideTabBar:(UITabBarController *) tabbarcontroller {
[self.navigationController setNavigationBarHidden:YES animated:YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.1];
for(UIView *view in tabbarcontroller.view.subviews)
{
if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 480, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 480)];
}
}else if([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 1024, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 1024)];
}
}
}
[UIView commitAnimations];
}
// Method shows the bottom and top bars
- (void) showTabBar:(UITabBarController *) tabbarcontroller {
[self.navigationController setNavigationBarHidden:NO animated:YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.1];
for(UIView *view in tabbarcontroller.view.subviews)
{
if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 430, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 436)];
}
}else if([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, 975, view.frame.size.width, view.frame.size.height)];
}
else
{
[view setFrame:CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, 980)];
}
}
}
[UIView commitAnimations];
}
Try this
you can hide tabbar contrller and navigation bar using animation like:-
-(IBAction)hide:(id)sender
{
[self hideShowBars];
}
- (void) hideShowBars
{
CGRect rect = self.navigationController.navigationBar.frame;
CGRect rect1 = self.tabBarController.tabBar.frame;
if(self.navigationController.navigationBar.isHidden)
{
[self.navigationController.navigationBar setHidden:NO];
rect.origin.y=self.view.frame.origin.y;
}
else
{
rect.origin.y=self.view.frame.origin.y-rect.size.height;
}
if(self.tabBarController.tabBar.isHidden)
{
[self.tabBarController.tabBar setHidden:NO];
rect1.origin.y=self.view.frame.size.height-rect1.size.height-rect1.size.height;
}
else
{
rect1.origin.y=self.view.frame.size.height;
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.50];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(hideShowBarsAnimationStopped)];
self.navigationController.navigationBar.frame=rect;
self.tabBarController.tabBar.frame = rect1;
[UIView commitAnimations];
}
- (void) hideShowBarsAnimationStopped
{
if(self.navigationController.navigationBar.frame.origin.y==self.view.frame.origin.y)
return;
if(!self.navigationController.navigationBar.isHidden)
{
[self.navigationController.navigationBar setHidden:YES];
}
if(!self.tabBarController.tabBar.isHidden)
{
[self.tabBarController.tabBar setHidden:YES];
}
}

List of Buttons lighting up at random

The method im trying to achieve is set a button to 0.5 alpha then back to 1 in the space of three seconds. After its executed 5 time on the four buttons the block of code is finished. Im struggling to find a way in which this can be achieved becasue right now the block below will be an infinite loop when i want it to be only executed through once.
int rand=random()%5;
switch (rand) {
case 1:{
[UIView beginAnimations:NULL context:NULL];
[UIView setAnimationDuration:5.0];
[btnYellow setAlpha:0.5];
[btnYellow setAlpha:1];
[UIView commitAnimations];
}
break;
case 2:
[UIView beginAnimations:NULL context:NULL];
[UIView setAnimationDuration:5.0];
[btnRed setAlpha:0.5];
[btnRed setAlpha:1];
[UIView commitAnimations];
break;
case 3:
[UIView beginAnimations:NULL context:NULL];
[UIView setAnimationDuration:5.0];
[btnBlue setAlpha:1];
[UIView commitAnimations];
case 4:
[UIView beginAnimations:NULL context:NULL];
[UIView setAnimationDuration:5.0];
[btnGreen setAlpha:1];
[UIView commitAnimations];
break;
case 5:
[UIView beginAnimations:NULL context:NULL];
[UIView setAnimationDuration:5.0];
[btnYellow setAlpha:1];
[UIView commitAnimations];
break;
}
As you are setting up the button's alpha to 0.5 and then to 1 immediately, the button's alpha won't animate. You can get an idea this snippet
[UIView animateWithDuration:0.3 animations:^{
[your_btn setAlpha:0.5];
} completion:^(BOOL finished) {
if(finished)
[self performSelector:#selector(revertAlphaToOne) withObject:nil afterDelay:0.5];
}];
And in that revertAlphaToOne Method, you can revert the button's alpha to 1 as
[UIView animateWithDuration:0.3 animations:^{
[your_btn setAlpha:1.0];
} completion:nil
}];
Adjust the time variables according to your likings And/Or call the second snippet in the first block's completion block itself.
Well, I've been using this site for several years, and I was due for a nice thorough answer to give back.
I built a project and accomplished the functionality you desired. Some notes:
-The key is recursion! You need to animate the fade out (using the function I provided) and, upon completion, animating the fade back in using the same function. Once completed, exercise recursion and prepare to call itself (beginAnimations) until all ints=5.
-I decided to fade from 1->.5->1 because the .5 alpha was annoying me. Shouldn't be hard to switch that around if you want it reversed.
-To make sure no button fades more than fives times, you need to declare and increment an int corresponding to each button.
-Use arc4random(), not random(). Random is for debugging as you will have the same result every time.
-Keep your switch case's straight; was hard to understand what you wanted to be different between them. On this note, use breaks and a default statement! Good luck employing all this into your app.
.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor blackColor]];
//declare and define button specifics
btnYellow = [UIButton buttonWithType: UIButtonTypeCustom];
[btnYellow setBackgroundColor: [UIColor yellowColor]];
btnYellow = [self buttonTraits:btnYellow];
[btnYellow setFrame: CGRectMake(20, 20, 280, 30)];
[btnYellow setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnYellow.titleLabel setFont:[UIFont boldSystemFontOfSize:14]];
btnRed = [UIButton buttonWithType: UIButtonTypeCustom];
[btnRed setBackgroundColor: [UIColor redColor]];
[btnRed setFrame: CGRectMake(20, 60, 280, 30)];
[btnRed setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnRed.titleLabel setFont:[UIFont boldSystemFontOfSize:14]];
btnBlue = [UIButton buttonWithType: UIButtonTypeCustom];
[btnBlue setFrame: CGRectMake(20, 100, 280, 30)];
[btnBlue setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnBlue.titleLabel setFont:[UIFont boldSystemFontOfSize:14]];
[btnBlue setBackgroundColor: [UIColor blueColor]];
btnGreen = [UIButton buttonWithType: UIButtonTypeCustom];
[btnGreen setFrame: CGRectMake(20, 140, 280, 30)];
[btnGreen setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btnGreen.titleLabel setFont:[UIFont boldSystemFontOfSize:14]];
[btnGreen setBackgroundColor: [UIColor greenColor]];
//add buttons to the view
[self.view addSubview:btnYellow];
[self.view addSubview:btnRed];
[self.view addSubview:btnBlue];
[self.view addSubview:btnGreen];
//set the counting ints to 0
yellowCount = 0, redCount = 0, blueCount = 0, greenCount = 0;
//run through the animations the first time
[self beginAnimations];
}
-(void)beginAnimations
{
if (!(yellowCount==5 && redCount==5 && blueCount == 5 && greenCount == 5))
{
//if you want 5 buttons, define another one, then the next line would be int rand=random()%6;
int rand=((arc4random()%5)+1); //arc4random gives 0-3; add 1 for 1-4
switch (rand) {
case 1:
{
//make sure this button hasn't gone through the process 5 times already
if (yellowCount<5)
{
//increment the button's count
yellowCount++;
//set up animation with 1.5 second duration (alpha decline from 1->.5)
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^{
[btnYellow setAlpha:.5];
} completion:^(BOOL finished)
{
if(finished==TRUE)
{
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^{
[btnYellow setAlpha:1];
} completion:^(BOOL finished) {
if(finished==TRUE)
{
[self beginAnimations];
}
}];
}
}];
}
else
{
//restart the animation hoping to get another button that hasn't gone 5 times yet
[self beginAnimations];
}
}
break; //you forgot a break here. can cause troubles
case 2:
{
if (redCount<5)
{
redCount++;
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^
{
[btnRed setAlpha:.5];
} completion:^(BOOL finished)
{
if(finished==TRUE)
{
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^{
[btnRed setAlpha:1];
} completion:^(BOOL finished) {
if(finished==TRUE)
{
[self beginAnimations];
}
}];
}
}];
}
else
{
[self beginAnimations];
}
}
break;
case 3:
{
if (blueCount<5)
{
blueCount++;
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^
{
[btnBlue setAlpha:.5];
} completion:^(BOOL finished)
{
if(finished==TRUE)
{
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^{
[btnBlue setAlpha:1];
} completion:^(BOOL finished) {
if(finished==TRUE)
{
[self beginAnimations];
}
}];
}
}];
}
else
{
[self beginAnimations];
}
}
break; //you forgot another break here. can cause troubles
case 4:
{
if (greenCount<5)
{
greenCount++;
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^
{
[btnGreen setAlpha:.5];
} completion:^(BOOL finished)
{
if(finished==TRUE)
{
[UIView animateWithDuration:1.5 delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^{
[btnGreen setAlpha:1];
} completion:^(BOOL finished) {
if(finished==TRUE)
{
[self beginAnimations];
}
}];
}
}];
}
else
{
[self beginAnimations];
}
}
break;
default:
{
//in case of an awry number, restart the process (be wary; infinite loop potential)
[self beginAnimations];
}
break; //it is of good practice to always have a default method in switch statements
}
}
else
{
//the process is complete
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
UIButton *btnYellow;
UIButton *btnRed;
UIButton *btnBlue;
UIButton *btnGreen;
int yellowCount;
int redCount;
int blueCount;
int greenCount;
}
#end
The problem is that in cases 1 and 2, you set the alpha value to 0.5 and then IMMEDIATELY back to 1. So when the animation starts, it is simply 1 and will remain so. Just delete the 2nd assignment, and the animation will change alpha to 0.5. PS: As already mentioned, the random number generated is from 0 to 4, and not from 1 to 5.