I have a UIViewController that i edit using IB. I put a UINavigationBar and a UISegmentedControl on the top and 3 UIViews under them. I want to be able to switch between the UIViews using an animation, but i only want to animate the UIViews, i want the navigationBar and athe segmentedControl to not move. I show the code how i do it now.
Any idea how i could only move the 3 views?
- (IBAction)segmentedControlValueChanged:(id)sender {
UISegmentedControl* segmentedControl = sender;
if(lastSelectedViewIndex != [segmentedControl selectedSegmentIndex]) {
CATransition *transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
if(lastSelectedViewIndex < [segmentedControl selectedSegmentIndex])
transition.subtype = kCATransitionFromLeft;
else
transition.subtype = kCATransitionFromRight;
transition.removedOnCompletion = YES; // force removal of animation when completed.
{
switch ([segmentedControl selectedSegmentIndex]) {
case 0:
[self.usageScenarioView setHidden:NO];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:0];
break;
case 1:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:NO];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:1];
break;
case 2:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:NO];
[self.pageControl setCurrentPage:2];
break;
}
}
lastSelectedViewIndex = [segmentedControl selectedSegmentIndex];
[self.view.layer addAnimation:transition forKey:nil];
}
}
Say your 3 views are named as view1, view2, view3. If you want to remove view1 and show view2 or view3, just do the existing code, but change
[self.view.layer addAnimation:transition forKey:nil];
into
[view1.layer addAnimation:transition forKey:nil];
that will animate the view1 only not the whole view. Similarly you can try,
[view2.layer addAnimation:transition forKey:nil];
[view3.layer addAnimation:transition forKey:nil];
more precisely, do like
transition.removedOnCompletion = YES; // force removal of animation when completed.
{
switch ([segmentedControl selectedSegmentIndex]) {
case 0:
[self.usageScenarioView setHidden:NO];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:0];
[self.usageScenarioView.layer addAnimation:transition forKey:nil];
break;
case 1:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:NO];
[self.whatItCoversView setHidden:YES];
[self.pageControl setCurrentPage:1];
[self.loginCredentialsView.layer addAnimation:transition forKey:nil];
break;
case 2:
[self.usageScenarioView setHidden:YES];
[self.loginCredentialsView setHidden:YES];
[self.whatItCoversView setHidden:NO];
[self.pageControl setCurrentPage:2];
[self.whatItCoversView.layer addAnimation:transition forKey:nil];
break;
}
}
lastSelectedViewIndex = [segmentedControl selectedSegmentIndex];
}
you can define a UIView* containerView in IB below your UISegmentedControl* segmentedControl which has the coordinates and position of the views you want.
Then you can inside that view you can do transition between the three UIViews you want with this function:
-(void) replacePreviousViewInContainerViewWith:(UIView*) newView {
[UIView transitionWithView:_containerView duration:0.7
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseIn
animations:^ {
[_containerView.subviews[0] removeFromSuperview];
[_containerView addSubview:newViewVC.view];
}
completion:^(BOOL finished) {
if (finished) {
NSLog(#"Now displaying %#.", [newView class]);
}
}];
[UIView commitAnimations];
}
and you call this function like this from where you want to change the Views:
UIViewController *newViewVC = [[UIViewController alloc] initWithNibName#"YOURNAME"];
newViewVC.view.frame = _containerView.frame;
[self replacePreviousViewInContainerViewWith: newViewVC.view];
Related
My problem is that I've 2 graphics animation one after the other, but they are done together.
I thought to write a method with completion block, but or I'm doing something wrong, or there is another way to do this.
The animation is a uiview that move out of the screen (closeView) and the default animation of viewcontroller closing.
I want the viewcontroller closes when is over the animation of view.
This is what i've done
- (void)closeViewWithCompletion:(void (^) (BOOL finish))handler {
[self closeView];
handler(YES);
}
-(void) closeView{
[self.myview setFrame:CGRectMake(
-self.myview.frame.size.width
, self.myview.frame.origin.y
, self.myview.frame.size.width
, self.view.frame.size.height
)];
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromRight];
[animation setDuration:.50];
[animation setDelegate:self];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
CALayer *layer = [self.myview layer];
[layer addAnimation:animation forKey:nil];
[UIView animateWithDuration:0.5 animations:^{
[greyMask setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0]];
} completion:^(BOOL finished) {
[greyMask removeFromSuperview];
}];
}
use:
[vc closeViewWithCompletion:^(BOOL finish) {
[navController popViewControllerAnimated:true];
}
];
call your handler block in completionBlock after your closeView Animation finishes.
- (void)closeViewWithCompletion:(void(^)(BOOL finish))handler {
//closeView Animation
[UIView animateWithDuration:duration delay:0 options: UIViewAnimationOptionCurveEaseOut animations:^{
//code for close view animation
} completion:^(BOOL finished) {
handler(YES) //call your handler in completion block
}];
}
[vc closeViewWithCompletion:^(BOOL finish) {
[self.navigationController popViewControllerAnimated:true];
}];
I'm trying to do an effect to open and close a view(menu) the view has origin x = 55.
the open effect works properly good, but the close effect has an problem:
the effect begins at x = 0 and my view is at x = 55...then the effect appears strange....
There's my code:
-(IBAction)menuClick:(id)sender {
if(!self.viewMais) {
CGRect screen = [[UIScreen mainScreen]bounds];
self.viewMais = [[UIView alloc]initWithFrame:CGRectMake(55, 0, screen.size.width-55, screen.size.height)];
[self.viewMais setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:self.viewMais];
[self open];
}
else {
[self close];
}
}
-(void)open {
CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromRight;
UIView *containerView = self.viewMais;
[containerView.layer addAnimation:transition forKey:nil];
}
-(void)close {
CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromLeft;
UIView *containerView = self.viewMais;
[containerView.layer addAnimation:transition forKey:nil];
[self performSelector:#selector(removerViewMais) withObject:nil afterDelay:0.3];
}
-(void) removerViewMais {
[self.viewMais removeFromSuperview];
self.viewMais = nil;
}
-(IBAction)showPicker{
if (!isPickerShow) {
[self.view endEditing:YES];
[UIView animateWithDuration:0.3 animations:^{
viewWithPicker.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-viewWithPicker.frame.size.height, 320, 555);
isPickerShow=YES;
}];
}
}
-(IBAction)hidePicker{
if (isPickerShow) {
[UIView animateWithDuration:0.3 animations:^{
viewWithPicker.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height , 320, 255);
isPickerShow=NO;
}];
}
}
I solved my problem with the following code in close menu
-(void)close {
CGRect destination = self.view.frame;
[UIView animateWithDuration:0.25 animations:^{
self.viewMais.frame = CGRectMake(destination.size.width, self.viewMais.frame.origin.y, self.viewMais.frame.size.width, self.viewMais.frame.size.height);
} completion:^(BOOL finished) {
[self.viewMais setHidden:YES];
[self.viewMais removeFromSuperview];
self.viewMais = nil;
}];
}
I'm trying to add a simple push animation between UIViewControllers using a custom segue.
(Without converting my controllers to use a UINavigationController)
The answers found so far work fine with a navigation controller, but fail when not using a navigation controller. (I am still reading and trying other answers I've seen here on stack-overflow)
My custom segue .m thanks to (ZHCustomSegue.m) Zakir on 7/5/12
#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"
#implementation SFCustomSegue
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = 4.0;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];
[sourceViewController presentViewController:[self destinationViewController] animated:NO completion:nil];
}
If I use a navigation controller as per Zakir's original example and replace the last 2 lines with:
[sourceViewController.navigationController.view.layer addAnimation:transition
forKey:kCATransition];
[sourceViewController.navigationController pushViewController:destinationController animated:NO];
It works....
Apple's doc says (notice not a navigation controller, and using a modal segue):
- (void)perform
{
// Add your own animation code here.
[[self sourceViewController] presentModalViewController:[self destinationViewController] animated:NO];
}
By NOT Working, I get no animation. In Zakir's code, using 4.0 seconds it takes 4.0 seconds and I get the animation I want. Of course, if I use a navigation controller a push tranition is the default. In this code, I get no animation and it takes 0 seconds.
Has anyone gotten custom segue animations to work with UIViewControllers?
Since you are calling presentViewController, the source view controller is sticking around. If you actually want to replace the source view controller with the destination view controller, so that the source gets dealloced, you can do this:
static UIImageView *screenShotOfView(UIView *view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
- (void)perform
{
UIViewController *source = (UIViewController *) self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
// Swap the snapshot out for the source view controller
UIWindow *window = source.view.window;
UIImageView *screenShot = screenShotOfView(source.view);
CGRect originalFrame = destination.view.frame;
BOOL animsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animsEnabled];
[UIView animateWithDuration:kAnimationDuration
animations:^{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished) {
[screenShot removeFromSuperview];
}];
}
It appears that my above code, is "nearly" working, but not quite.
The main issue at hand is likely when the final step is called.
My working code looks like this:
#import "SFCustomSegue.h"
#import "QuartzCore/QuartzCore.h"
#implementation SFCustomSegue
static const float delay = 0.5f;
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
[sourceViewController.view addSubview:destinationController.view];
CATransition* transition = [CATransition animation];
transition.duration = delay;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[sourceViewController.view.layer addAnimation:transition forKey:kCATransition];
[self performSelector:#selector(performDone:) withObject:destinationController afterDelay:delay];
}
- (void)performDone:(id)viewController{
UIViewController *destination = (UIViewController*)viewController;
[destination.view removeFromSuperview];
[[self sourceViewController] presentViewController:destination animated:NO completion:nil];
}
#end
The key to resolving this was adding the view for the transition (line 13), then removing it (line 28) and adding it back (line 29) using presentViewController:animated:completion:.
I am still "stuck" with using only the regular CATransition transition types, but that's good enough for me for now, since I want to use kCATransitionPush.
I have merged the ideas from both answers to get this code (working good):
(the answers from above don't count in the orientation of device so they won't work in landscape, for example)
(the variant with CATransitions wasn't appropriate because there was a gap between two views while the one pushed the another)
#import "PushSegue.h"
/*
#interface TransitionDelegate : NSObject
- (id) initWithScreenshot: (UIView*) screenshot;
#end
*/
#implementation PushSegue
- (void) perform
{
UIViewController* source = (UIViewController*) self.sourceViewController;
UIViewController* destination = (UIViewController*) self.destinationViewController;
const BOOL iPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
float animationDuration = iPad ? 0.5 : 0.3;
// Swap the snapshot out for the source view controller
UIWindow* window = source.view.window;
UIImageView* screenShot = screenShotOfView(source.view);
// accord to device orientation
float rotation;
//NSString* transitionType;
CGRect originalFrame = destination.view.frame;
CGRect destinationFrame = destination.view.frame;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
rotation = M_PI;
destinationFrame.origin.x -= source.view.bounds.size.width;
//transitionType = kCATransitionFromLeft;
break;
case UIInterfaceOrientationLandscapeLeft:
rotation = M_PI + M_PI_2;
destinationFrame.origin.y -= source.view.bounds.size.width;
//transitionType = kCATransitionFromBottom;
break;
case UIInterfaceOrientationLandscapeRight:
rotation = M_PI_2;
destinationFrame.origin.y += source.view.bounds.size.width;
//transitionType = kCATransitionFromTop;
break;
default:
rotation = 0;
destinationFrame.origin.x += source.view.bounds.size.width;
//transitionType = kCATransitionFromRight;
break;
}
screenShot.transform = CGAffineTransformMakeRotation(rotation);
// reposition after rotation
CGRect screenshotFrame = screenShot.frame;
screenshotFrame.origin.x = 0;
screenshotFrame.origin.y = 0;
screenShot.frame = screenshotFrame;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
screenshotFrame.origin.x += screenShot.bounds.size.width;
break;
case UIInterfaceOrientationLandscapeLeft:
screenshotFrame.origin.y += screenShot.bounds.size.width;
break;
case UIInterfaceOrientationLandscapeRight:
screenshotFrame.origin.y -= screenShot.bounds.size.width;
break;
default:
screenshotFrame.origin.x -= screenShot.bounds.size.width;
break;
}
// swap the view with its screenshot
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
/*
CATransition* transition = [CATransition animation];
transition.duration = animationDuration;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = transitionType;
transition.delegate = [[TransitionDelegate alloc] initWithScreenshot:screenShot];
[window addSubview:destination.view];
[screenShot.window.layer addAnimation:transition forKey:nil];
*/
const BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
destination.view.frame = destinationFrame;
}
[UIView setAnimationsEnabled:animationsEnabled];
[UIView animateWithDuration:animationDuration
animations:^
{
destination.view.frame = originalFrame;
screenShot.frame = screenshotFrame;
}
completion:^(BOOL finished)
{
[screenShot removeFromSuperview];
}];
/*
const BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animationsEnabled];
[UIView animateWithDuration:animationDuration
animations:^
{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished)
{
[screenShot removeFromSuperview];
}];
*/
/*
CATransition* transition = [CATransition animation];
transition.duration = animationDuration;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
switch ([UIApplication sharedApplication].statusBarOrientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
transition.subtype = kCATransitionFromLeft;
break;
case UIInterfaceOrientationLandscapeLeft:
transition.subtype = kCATransitionFromBottom;
break;
case UIInterfaceOrientationLandscapeRight:
transition.subtype = kCATransitionFromTop;
break;
default:
transition.subtype = kCATransitionFromRight;
break;
}
[source.view.window.layer addAnimation:transition forKey:nil];
[source presentViewController:destination animated:NO completion:nil];
*/
}
static UIImageView* screenShotOfView(UIView* view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView* screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
#end
/*
#implementation TransitionDelegate
{
UIView* screenshot;
}
- (id) initWithScreenshot: (UIView*) screenshot
{
if (self = [super init])
{
self->screenshot = screenshot;
}
return self;
}
- (void) animationDidStop: (CAAnimation*) theAnimation
finished: (BOOL) flag
{
[screenshot removeFromSuperview];
}
#end
*/
I am using this method:
[UIView transitionWithView: duration: options: animations: completion:];
to control a back animation on a segue. Precisely using this code:
[UIView transitionWithView:self.view.superview
duration:2.0
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[self dismissModalViewControllerAnimated:NO];
}
completion:nil];
That works, but what I really want is not a Flip, it is a Push. In other words I want to see the views sliding from right to left, one replacing the other.
Unfortunately there is no UIViewAnimationOptionTransitionPushFromRight.
Therefore my question: how can I get the effect I want?
If you are using a navigationController, you can do something like this:
- (void)perform
{
UIViewController *source = (UIViewController*)[self sourceViewController];
UIViewController *destination = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = .25;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[source.navigationController.view.layer addAnimation:transition
forKey:kCATransition];
[source.navigationController pushViewController:destination animated:NO];
}
If you don't want to use a UINavigationController, and you want the source view controller to go away as opposed to using presentViewController, and you don't want the fade out animation caused by using kCATransitionPush, then the following solution will work. This also works if your views are transparent and you do not want the containing background to animate.
static UIImageView *screenShotOfView(UIView *view)
{
// Create a snapshot for animation
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
[view.layer renderInContext:context];
UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return screenShot;
}
- (void)perform
{
UIViewController *source = (UIViewController *) self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
// Swap the snapshot out for the source view controller
UIWindow *window = source.view.window;
UIImageView *screenShot = screenShotOfView(source.view);
CGRect originalFrame = destination.view.frame;
BOOL animsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
{
window.rootViewController = destination;
[window addSubview:screenShot];
[source.view removeFromSuperview];
CGRect frame = destination.view.frame;
frame.origin.x += source.view.bounds.size.width;
destination.view.frame = frame;
}
[UIView setAnimationsEnabled:animsEnabled];
[UIView animateWithDuration:kAnimationDuration
animations:^{
destination.view.frame = originalFrame;
CGRect frame = screenShot.frame;
frame.origin.x -= screenShot.bounds.size.width;
screenShot.frame = frame;
}
completion:^(BOOL finished) {
[screenShot removeFromSuperview];
}];
}
I was wondering if it was possible to apply a flashing animation to a UIButton. I have searched but only found the code for a pulse animation which changes the size of my UIButton continuously. I instead was thinking about some kind of flashing animation to alert the user he has to press the button. The only approach to the problem I can think of is by changing the alpha constantly using:
[self setAlpha:0.5];
...but it won't be as visible as a flashing button.
Perhaps not the best way, and doesn't really allow you to stop the flashing... but this is simple, works, and does not hinder user interaction:
- (void)viewDidLoad
{
[self flashOn:myButton];
}
- (void)flashOff:(UIView *)v
{
[UIView animateWithDuration:.05 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^ {
v.alpha = .01; //don't animate alpha to 0, otherwise you won't be able to interact with it
} completion:^(BOOL finished) {
[self flashOn:v];
}];
}
- (void)flashOn:(UIView *)v
{
[UIView animateWithDuration:.05 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^ {
v.alpha = 1;
} completion:^(BOOL finished) {
[self flashOff:v];
}];
}
Reading all the answers so far I found following solution. It repeats a number of times and does the job for me.
CGFloat oldAlpha = v.alpha;
CGFloat minAlpha = 0.2;
enum UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionCurveEaseInOut;
[UIView animateWithDuration:.3 delay:0.3 options:options animations:^{
[UIView setAnimationRepeatCount:4];
v.alpha = minAlpha;
}
completion:^(BOOL finished) {
v.alpha = oldAlpha;
}];
Try this one:
call this method when you want blink/flash the button
blinkTimer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(toggleButtonImage:) userInfo:nil repeats:YES];
and write this method
- (void)toggleButtonImage:(NSTimer*)timer
{
if(toggle)
{
[blinkBtn setImage:[UIImage imageNamed:#"ButtonImage.png"] forState: UIControlStateNormal];
}
else
{
[blinkBtn setImage:[UIImage imageNamed:#"ButtonImage1.png"] forState: UIControlStateNormal];
}
toggle = !toggle;
}
in .h write this one
NSTimer *blinkTimer;
BOOL toggle;
and invalidate the timer where you want to stop the flashing/blinking
[blinkTimer invalidate];
Maybe you could have two different .png files [buttons] that cycle back and forth in a loop, assign the same action to them, and have this kick off whenever a certain condition is met. I'd write the code out but it would likely be full of errors. Have you tried something like that?
I did this by creating my own control, subclassing UIControl since Apple doesn't recommend screwing with the view hierarchy of UIButton. I add a background imageView representing the standard background image of the button, and a "glowing" imageView above the background to represent the lit-up state, and toggle its opacity to make it pulse.
I additionally toggle the layer's shadow opacity to make it glow.
Initializing Code:
- (void)TS_commonButtonInit
{
UIImage *shoutoutBackground = [UIImage imageNamed:#"root-navigation-bar-share-button"];
UIImage *shoutoutHighlightedBackground = [UIImage imageNamed:#"root-navigation-bar-share-button-highlighted"];
UIImage *shoutoutPulseImage = [UIImage imageNamed:#"root-navigation-bar-share-button-glowing"];
shoutoutBackground = [shoutoutBackground stretchableImageWithLeftCapWidth:7 topCapHeight:0];
shoutoutHighlightedBackground = [shoutoutHighlightedBackground stretchableImageWithLeftCapWidth:7 topCapHeight:0];
shoutoutPulseImage = [shoutoutPulseImage stretchableImageWithLeftCapWidth:7 topCapHeight:0];
[[self backgroundView] setImage:shoutoutBackground];
[[self backgroundView] setHighlightedImage:shoutoutHighlightedBackground];
[self setGlowingImage:shoutoutPulseImage];
[self setExclusiveTouch:YES];
[self addSubview:[self backgroundView]];
[self addSubview:[self glowingImageView]];
[[self layer] setShadowColor:[[UIColor colorWithHexString:#"ffc521" alpha:1] CGColor]];
[[self layer] setShadowOpacity:0];
[[self layer] setShadowRadius:5];
[[self layer] setShadowOffset:CGSizeMake(0, 0)];
[[self layer] setShadowPath:[[UIBezierPath bezierPathWithRoundedRect:[self bounds] cornerRadius:6] CGPath]];
}
Pulsing Code:
- (void)pulse:(NSInteger)numberOfTimes
{
CGFloat pulseLength = .8;
[[self glowingImageView] setAlpha:0];
CABasicAnimation *pulseAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
[pulseAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[pulseAnimation setDuration:pulseLength];
[pulseAnimation setRepeatCount:numberOfTimes];
[pulseAnimation setAutoreverses:YES];
[pulseAnimation setFromValue:#(0)];
[pulseAnimation setToValue:#(1)];
[pulseAnimation setRemovedOnCompletion:YES];
[[self layer] setShadowOpacity:0];
CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:#"shadowOpacity"];
[shadowAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[shadowAnimation setDuration:pulseLength];
[shadowAnimation setRepeatCount:numberOfTimes];
[shadowAnimation setAutoreverses:YES];
[shadowAnimation setFromValue:#(0)];
[shadowAnimation setToValue:#(1)];
[shadowAnimation setRemovedOnCompletion:YES];
[[[self glowingImageView] layer] addAnimation:pulseAnimation forKey:#"opacity"];
[[self layer] addAnimation:shadowAnimation forKey:#"shadowOpacity"];
}