UIButton flashing animation - ios

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"];
}

Related

LongPressGesture with custom delegates being called twice. Is it because of my delegates?

I'm having some trouble getting my UILongPressGestureRecognizer to only call itself once. I have a button that I longpress down for the duration of a recording and then upon release, it's supposed to stop the recording. For some reason when I hold down the button, it calls UIGestureRecognizerBegan, then UIGestureRecognizerEnded twice. Basically, there are two recordings. One on the longpress and then on the release, it ends, but calls it again thus creating two recordings. I have one button that has multiple protocols firing at the same time and I'm curious if this is a problem. I took a look at this link:
UILongPressGestureRecognizer gets called twice when pressing down
but I'm not getting the results I want.
The button also zooms when the longpressstate begins and it goes back to it's regular size when the longpressstate ends so I have some animation code below as well that's happening all at the same time.
ButtonView.m
- initWithFrame
[self.recordButton addTarget:self action:#selector(longPress:) forControlEvents:(UIControlEventTouchDown)];
[self addSubview:self.recordButton];
self.longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
self.longPressGesture.allowableMovement = 100.0;
self.longPressGesture.numberOfTouchesRequired = 1;
[self.recordButton addGestureRecognizer:self.longPressGesture];
}
- (void)longPress:(UILongPressGestureRecognizer *)gr {
if ([self.delegate respondsToSelector:#selector(buttonView:didTryToZoom:)]) {
[self.delegate buttonView:self didTryToZoom:self.recordButton];
}
if ([self.delegate respondsToSelector:#selector(buttonView:didTryToShake:)]) {
[self.delegate buttonView:self didTryToShake:self.recordButton];
}
if ([self.delegate respondsToSelector:#selector(recordButtonPressedWithGesture:)]) {
[self.delegate recordButtonPressedWithGesture:(UIGestureRecognizerStateBegan)];
}
if ([self.delegate respondsToSelector:#selector(recordButtonReleasedWithGesture:)]) {
[self.delegate recordButtonReleasedWithGesture: (UIGestureRecognizerStateEnded)];
}
}
In my view controller, I'm calling my delegates from my ButtonView class and adapting to the protocols by calling it in -viewDidLoad.
Here are my delegate methods that I'm calling from my ButtonView class:
ViewController.m
- (void)recordButtonReleasedWithGesture:(UIGestureRecognizerState)state {
if (state == UIGestureRecognizerStateEnded) {
[self audioRecorderDidFinishRecording:self.recorder successfully:YES];
NSData *data = [NSData dataWithContentsOfURL:self.recorder.url];
[data writeToFile:[NSHomeDirectory() stringByAppendingString:[NSString stringWithFormat:#"%#", [[AudioController sharedInstance] filePath]]] atomically:YES];
[[RecordingController sharedInstance] addRecordingWithURL:[[AudioController sharedInstance] filePath]
andIDNumber:[[AudioController sharedInstance] randomIDNumber]
andDateCreated:[[AudioController sharedInstance] createdAtDate]
andFetchDate:[[AudioController sharedInstance] fetchDate]
andSimpleDate:[[AudioController sharedInstance] simpleDateString]
andGroupName:[[AudioController sharedInstance] groupName]];
[[RecordingController sharedInstance] save];
}
}
- (void)recordButtonPressedWithGesture:(UIGestureRecognizerState)state {
if (state == UIGestureRecognizerStateBegan) {
self.buttonView.playButton.enabled = NO;
self.recorder.delegate = self;
self.recorder = [[AudioController sharedInstance] recordAudioToDirectory];
}
}
- (void)buttonView:(ButtonView *)view didTryToShake:(UIButton *)button {
if (self.containerView.state == ButtonStateNone) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setDuration:0.1];
[animation setRepeatCount:3];
[animation setAutoreverses:YES];
[animation setFromValue:[NSValue valueWithCGPoint:
CGPointMake([self.buttonView.recordButton center].x + 20, [self.buttonView.recordButton center].y)]];
[animation setToValue:[NSValue valueWithCGPoint:
CGPointMake([self.buttonView.recordButton center].x - 20, [self.buttonView.recordButton center].y)]];
[[self.buttonView.recordButton layer] addAnimation:animation forKey:#"position"];
}
}
- (void)buttonView:(ButtonView *)view didTryToZoom:(UIButton *)button {
if (self.containerView.state == ENUMS) {
if (view.longPressGesture.state == UIGestureRecognizerStateBegan) {
[UIView animateWithDuration:.5f
delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.containerView.hidden = YES;
self.buttonView.recordButton.transform = CGAffineTransformScale(self.buttonView.recordButton.transform, 3, 3);
self.buttonView.recordButton.alpha = .7;
} completion:nil];
}
if (view.longPressGesture.state == UIGestureRecognizerStateEnded) {
[UIView animateWithDuration:.25
delay:0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut
animations:^{
self.buttonView.recordButton.transform = CGAffineTransformScale(CGAffineTransformIdentity, .7, .7);
self.buttonView.recordButton.alpha = 1;
} completion:^(BOOL finished) {
[UIView animateWithDuration:.15
delay:0
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.buttonView.recordCompleteLabel.hidden = NO;
[NSTimer scheduledTimerWithTimeInterval:.65
target:self
selector:#selector(hideLabel)
userInfo:nil
repeats:NO];
self.buttonView.recordButton.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.containerView.hidden = NO;
//[self.containerView setState:ButtonStateNone];
[self noneState:ButtonStateNone];
self.buttonView.recordButton.backgroundColor = [UIColor blueColor];
I'm positive I'm missing something really silly and I'm just not seeing it. Are there any more seasoned developers that can identify why the long press is getting called twice? I'd greatly appreciate any help.
I found out that I had been calling it in another subclass of UIView. However, I still had a problem with it creating duplicate recordings. I dove a bit deeper and I realized that I had a singleton controller that was calling the AVAudioRecorder and I was also instantiating another instance of AVAudioRecorder by creating a property of AVAudioRecorder inside of the ViewController and setting it to the singleton controller's AVAudioRecorder. Many thanks to Suttikeat.

CATransition black color issue when push right to left

May be this types of issue occurred with many developer, I also tried to find on Google but there is no luck to solve my problem.
In my app rootViewController set on window dynamically, I mean user can change root view at run time application. So first I am removing current rootViewController from UIWindow and then I set new rootViewController on UIWindow with specific animation.
This animation done by CATransition with transition.type = kCATransitionPush;.
My app working very well except animation of new rootViewController. As above I said that I used kCATransitionPush with CATransition
EDITED:
Below is my code:
-(void) changeRootViewController
{
for(UIView *subViews in self.window.rootViewController.view.subviews)
[subViews removeFromSuperview];
[self.window.rootViewController.view removeFromSuperview];
self.window.rootViewController.view = nil;
[self.window.rootViewController removeFromParentViewController];
MainRootViewController *mainRootVC = [[MainRootViewController alloc] init];
CATransition *animation = [CATransition animation];
[animation setDuration:0.4];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromRight];
animation.fillMode = kCAFillModeForwards;
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
self.window.rootViewController = mainRootVC;
[[self.window layer] addAnimation:animation forKey:nil];
}
I also tried to set clearColor and whiteColor of background of UIWindow. But not working.
My Problem is : While animation is process (right to left) I am getting black shadow. So anybody can help me to hide this black shadow? Please give you golden suggestion.
Thanks.
Add in the delegate (didFinishLaunchingWithOptions method) these two lines :
self.window.backgroundColor = [UIColor whiteColor];
self.window.tintColor = [UIColor whiteColor];
I was having same requirement, I have 3 images of full screen size at splash screen which should slide from right to left. But used of CATransition doesn't solve the issue. So I have used scrollview with paging. Added 3 images in scrollview and then start animation.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
size = [UIScreen mainScreen].bounds.size;
imageArray = [[NSMutableArray alloc] init];
[imageArray addObject:[UIImage imageNamed:#"splashScreen1.png"]];
[imageArray addObject:[UIImage imageNamed:#"splashScreen2.png"]];
[imageArray addObject:[UIImage imageNamed:#"splashScreen3.png"]];
self.splashScrollView.frame = CGRectMake(0,0,size.width,size.height);
self.splashScrollView.contentSize = CGSizeMake(size.width * imageArray.count, size.height);
self.splashScrollView.pagingEnabled = YES;
CGFloat xPos = 0.0;
for (UIImage *image in imageArray) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(xPos, 0.0, size.width, size.height);
[self.splashScrollView addSubview:imageView];
xPos += size.width;
}
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(startAnimation) userInfo:nil repeats:NO];
}
This(startAnimation) method scrolled scrollView upto the width of image, on completion of first image animation, I have started second image animation which scrolled to the edge of scrollview width.
-(void) startAnimation{
[UIView animateWithDuration:2.0
animations:^{
self.splashScrollView.contentOffset = CGPointMake(size.width, 0.0);
} completion:^(BOOL finished) {
[UIView animateWithDuration:2.0
animations:^{
self.splashScrollView.contentOffset = CGPointMake((self.splashScrollView.contentSize.width - CGRectGetWidth(self.splashScrollView.frame)), 0.0);
} completion:^(BOOL finished) {
//At animation end go to login
}];
}];
}

How can i change view after an animation?

I need your help.
In my app i press a button and an animation starts. After this animation i want to change automatically view without pressing any other button.
How can i do?
Here my code, i think i'm in the correct way, but the animation doesn't start, changes only view and xcode doesn't give me an error message.
- (IBAction)button {
[UIView animateWithDuration:2.5f animations:^{
NSArray *playAnimation;
playAnimation= [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#“image1.png"],
[UIImage imageNamed:#“image2.png"],
[UIImage imageNamed:#"image3.png"],
[UIImage imageNamed:#"image4.png"],
[UIImage imageNamed:#"image5.png"],
[UIImage imageNamed:#"image6.png"],
[UIImage imageNamed:#"image7.png"],
Nil];
}
completion:^(BOOL finished) {
UIStoryboard *Scene1 = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *Sc1 = [Scene1 instantiateViewControllerWithIdentifier:#"ViewController"];
[self presentViewController:Sc1 animated:NO completion:nil];
}];
}
Many thanks for your help
Most of the animate* methods have sibling-forms that take a completion block. Use the completion to do whatever you'd like.
e.g.
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;
-- versus --
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Depends on how you started the animation. Read up on chaining UIView animations for a good overview of the subject.
If you're using UIView animation, set your animation delegate and an animationDidStopSelector, e.g.:
[UIView beginAnimations:#"Grow" context:self.btn];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(doneAnimating:finished:context:)];
// Do stuff you want animated
[UIView commitAnimations];
If you're using CABasicAnimation, same thing, just a different interface - set the delegate and animationDidStop selector, e.g.:
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.scale.y"];
anim.delegate = self;
anim.animationDidStop = #selector(doneAnimating:finished:context:)];
anim.fromValue = [NSNumber numberWithDouble:0.01];
anim.toValue = [NSNumber numberWithDouble:1.0];
anim.duration = 0.5f;
[layer addAnimation:anim forKey:#"grow"];
In either case, -doneAnimating will get called when the animation has finished:
-(void)doneAnimating:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
// Do whatever you want next
}
You can do basic UIView animations. These include completion blocks in which you can push/present views.
[UIView animateWithDuration:1.5f animations:^{
// Perform animation here.
// ex.
[label setFrame:CGRectMake(0, 0, 320, 50)];
} completion:^(BOOL finished) {
// Show new view.
[self.navigationController pushViewController:[[UIViewController alloc] init] animated:YES];
}];

Change Background Image and animate an UIButton when tapped iOS 7 [duplicate]

This question already has answers here:
Animating only the image in UIBarButtonItem
(4 answers)
Closed 8 years ago.
I have an UIButton created by the following code
button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:#"refresh_icon.png"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"reload_selected.png"] forState:UIControlStateSelected];
[button addTarget:self action:#selector(buttonAction:)forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 20 , 20)];
Here when it's clicked the selector buttonAction is called and I am animating the button by a 360 degree rotation like this
- (void) buttonAction : (id) sender {
NSLog(#"Reload Button Clicked");
[self runSpinAnimationOnView:self.button duration:1.0 rotations:1.0 repeat:0];
}
- (void) runSpinAnimationOnView:(UIView*)view duration:(CGFloat)duration rotations:(CGFloat)rotations repeat:(float)repeat;
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 /* full rotation*/ * rotations * duration ];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = repeat;
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
It's working good but it's not that I actually want. I want that , when my button is clicked then button will take the image "reload_selected.png" (which I set for UIControlStateSelected) as background and then run the animation. When the animation is finished then button will have to return to it's actual background ("refresh_icon.png") again.
Can anyone suggest some change in code that will help me achieve the action I described above ? Thanks in advance for your help.
Can you try like this,
[UIView animateWithDuration:YOURDURATION
delay:0
options:(UIViewAnimationOptionCurveLinear)
animations:^ {
[button setImage:[UIImage imageNamed:#"reload_selected.png"] forState:UIControlStateSelected];
button.transform = CGAffineTransformRotate(CGAffineTransformIdentity,M_PI * 2);
}
completion:^(BOOL finished) {
[button setImage:[UIImage imageNamed:#"refresh_icon.png"] forState:UIControlStateNormal];
}
];
Instead of using Core Animation, you can use animateWithDuration:animations:completion: method of UIView. This method allows you to specify a completion block whick you can use to reset the image once the animation is completed.
EDIT:
Do something like
[view animateWithDuration:duration animations:^{
//Do your animations here
} completion: ^(BOOL finished){
if(finished) {
//reset the image
}
}];
To specify the animations you might want to animate the view's transform property using CGAffineTransformRotate.

Independent running text line on independent thread

UPDATED
I want to make running text line, which way is better to make it?
-(void)viewDidLoad
{
CGSize mySize = CGSizeZero;
mySize = [kGrindFMRunningText sizeWithFont:[UIFont fontWithName:#"Verdana" size:16] constrainedToSize:CGSizeMake(4000, 30) lineBreakMode:UILineBreakModeWordWrap];
runningText = [[UIScrollView alloc] initWithFrame:CGRectMake(60, -5, 260, 50)];
grind_fm_text = [[UILabel alloc] initWithFrame:CGRectMake(0, 15, mySize.width, 30)];
[grind_fm_text setUserInteractionEnabled:NO];
[grind_fm_text setBackgroundColor:[UIColor clearColor]];
[grind_fm_text setTextColor:[UIColor whiteColor]];
[grind_fm_text setFont:[UIFont fontWithName:#"Verdana" size:16]];
[grind_fm_text setText:kGrindFMRunningText];
[runningText addSubview:grind_fm_text];
[grind_fm_text release];
[self animate];
}
- (void)animate
{
[UIView animateWithDuration:10.0 delay:0. options:UIViewAnimationOptionCurveLinear|UIViewAnimationOptionBeginFromCurrentState animations:^{
grind_fm_text.frame = CGRectMake(-grind_fm_text.frame.size.width, 15, grind_fm_text.frame.size.width, grind_fm_text.frame.size.height);
// Do your animation in one direction until text is gone
} completion:^(BOOL finished){
grind_fm_text.frame = CGRectMake(260, 15, grind_fm_text.frame.size.width, grind_fm_text.frame.size.height);
// Move scroll position back to original position
[self animate]; // Then call animate again to repeat
}];
}
-(void)songChange
{
CGSize mySize = CGSizeZero;
mySize = [result sizeWithFont:[UIFont fontWithName:#"Verdana" size:16] constrainedToSize:CGSizeMake(4000, 30) lineBreakMode:UILineBreakModeWordWrap];
grind_fm_text.frame = CGRectMake(grind_fm_text.frame.origin.x, 15, mySize.width, 30);
grind_fm_text.text = result;;
[self animate];
}
- (void)startStopStream {
[streamer stop];
//[bufferIndicator stopAnimating];
[CATransaction begin];
[self.view.layer removeAllAnimations];
[CATransaction commit];
grind_fm_text.text = kGrindFMRunningText;
CGSize mySize = CGSizeZero;
mySize = [kGrindFMRunningText sizeWithFont:[UIFont fontWithName:#"Verdana" size:16] constrainedToSize:CGSizeMake(4000, 30) lineBreakMode:UILineBreakModeWordWrap];
grind_fm_text.frame = CGRectMake(grind_fm_text.frame.origin.x, 15, mySize.width, 30);
[self animate];
}
[CATransaction begin]; [myView.layer removeAllAnimations]; [CATransaction commit]; doesn't work for me. UIViewAnimationOptionCurveLinear|UIViewAnimationOptionBeginFromCurrentState works strangely: first, it doesn't do completion block as i see, or maybe it does, but animation doesn't start again. And If i click button nothing happening, but when i click it second time, animation begins from another direction and slows until freeze.
You should be able to do this more simply with nested UIView animation blocks. In the animation block have it scroll in the one direction, in the completion block have it scroll in the other direction and in that animation's completion block have it call your animate function over again so it repeats.
Something like this:
- (void)animate
{
[UIView animateWithDuration:10.0 delay:0. options:UIViewAnimationOptionCurveLinear animations:^{
// Do your animation in one direction
} completion:^(BOOL finished){
[UIView animateWithDuration:10.0 delay:0. options:UIViewAnimationOptionCurveLinear animations:^{
// Do your animation in the other direction
} completion:^(BOOL finished){
[self animate];
}];
}];
}
Or if you want it to scroll all the way across then do it again, something like:
- (void)animate
{
[UIView animateWithDuration:10.0 delay:0. options:UIViewAnimationOptionCurveLinear animations:^{
// Do your animation in one direction until text is gone
} completion:^(BOOL finished){
// Move scroll position back to original position
[self animate]; // Then call animate again to repeat
}];
}
It sounds like by default, animations use the UIViewAnimationOptionCurveEaseInOut animation option. You want the UIViewAnimationOptionCurveLinear option. I've updated my code above to include that.
As per an answer to this question: Cancel a UIView animation? you should be able to cancel the animation by calling [myView.layer removeAllAnimations]; where myView is the scroll view being animated. Make sure to import <QuartzCore/QuartzCore.h> at the top to access CALayer methods.
Edit: you may need to call it like this to make sure it runs immediately and not after the next runloop iteration:
[CATransaction begin];
[myView.layer removeAllAnimations];
[CATransaction commit];
Or if that still doesn't work, then likely just changing the options parameters in the UIView method calls to UIViewAnimationOptionCurveLinear|UIViewAnimationOptionBeginFromCurrentState should work, with no need to call removeAllAnimations. In fact, try that first.

Resources