I have UIButton, that i wish to rotate in one direction for 180 degrees, and back also for 180 degrees. I have been doing animations for a while using CABasicAnimation, but this time i wanted to do it with transforms. Here is the code that i have written:
- (IBAction)blurAction:(id)sender {
if (self.blurActive) {
[UIView animateWithDuration:0.1 animations:^{
self.blurView.alpha = 0;
self.blurButton.transform = CGAffineTransformMakeRotation(M_PI);
} completion:^(BOOL finished) {
self.blurActive = NO;
}];
}
else {
[UIView animateWithDuration:0.1 animations:^{
self.blurView.alpha = 1;
self.blurButton.transform = CGAffineTransformMakeRotation(-M_PI);
} completion:^(BOOL finished) {
self.blurActive = YES;
}];
}
}
It works the first time, but second time i press the button, nothing happens. Can someone explain what I am doing wrong here?
The easiest way to achieve this effect is just to rotate a tiny, tiny amount less than 180°:
- (IBAction)toggle:(UIButton *)sender {
[UIView animateWithDuration:0.2 animations:^{
if (CGAffineTransformEqualToTransform(sender.transform, CGAffineTransformIdentity)) {
sender.transform = CGAffineTransformMakeRotation(M_PI * 0.999);
} else {
sender.transform = CGAffineTransformIdentity;
}
}];
}
Result:
Here is the Swift3 code for #rob mayoff solution.
#IBAction func fooButton(_ sender: Any) {
UIView.animate(withDuration:0.1, animations: { () -> Void in
if sender.transform == .identity {
sender.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI * 0.999))
} else {
sender.transform = .identity
}
})
}
Swift4
M_PI is deprecated and replaced with Double.pi
#IBAction func fooButton(_ sender: Any) {
UIView.animate(withDuration:0.1, animations: { () -> Void in
if sender.transform == .identity {
sender.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi * 0.999))
} else {
sender.transform = .identity
}
})
}
M_PI and -M_PI will have the same visual effect
The turning back to it's original position should be
self.blurButton.transform = CGAffineTransformMakeRotation(0);
--EDIT--
To animate the counter clockwise rotation, you can use -2*M_PI
self.blurButton.transform = CGAffineTransformMakeRotation(-2*M_PI);
Turning UIButton 180º
UIButton.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
Turning UIButton 0º or back again
UIButton.transform = CGAffineTransform(rotationAngle: 0)
Example to turn animate
if cardViewModel.getBottomMenuVisibility() {
[UIView .transition(
with: bottomMenuView,
duration: 0.3,
options: .transitionCrossDissolve,
animations: {
// Turn UIButton upside down
self.bottomMenu.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
},
completion: nil)]
}
else {
[UIView .transition(
with: bottomMenuView,
duration: 0.3,
options: .transitionCrossDissolve,
animations: {
// Turn UIButton back to normal
self.bottomMenu.transform = CGAffineTransform(rotationAngle: 0)
},
completion: nil)]
}
You can check on my code this example :
https://github.com/issuran/Scrum-Geek-Poker/blob/0d032a4b4edcf75cebae1524131f4fe6be3a5edf/Scrum%20Geek%20Poker/Features/CardCollection/View/MainScreenViewController.swift
To 'unrotate' the view set the transform to the constant CGAffineTransformIdentity , which is a transform of zero. This is the beauty of working with transforms, you don't need to calculate the way back, just undo what you already did.
You can not actually achieve this with transforms as transforms holds only information about end state. They do not tell us which direction we should use to make e.g. the rotation.
The best option to achieve 180 degrees forth and back rotation behaviour is to use CABasicAnimation.
Here's the example:
func rotateImage(forward: Bool) {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.fromValue = forward ? 0.0 : CGFloat.pi
rotationAnimation.toValue = forward ? CGFloat.pi : 0.0
rotationAnimation.fillMode = kCAFillModeForwards
rotationAnimation.isRemovedOnCompletion = false
rotationAnimation.duration = 0.5
handleImageView.layer.add(rotationAnimation, forKey: "rotationAnimation")
}
Swift 4+
#IBAction private func maximizeButtonAction(_ sender: UIButton) {
UIView.animate(withDuration: 0.2, animations: {
sender.transform = sender.transform == .identity ? CGAffineTransform(rotationAngle: CGFloat(Double.pi * 0.999)) : .identity
})
}
I have a custom animation for when the tabs are selected, but instead I want that animation to only occur for when a certain tab is clicked. I am guessing it has to do with the instantiation of the toView. Error http://puu.sh/n5VuA/c3e886957e.png
class TransitioningObject: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
// Get the "from"nd "to" views
let fromView : UIView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView : UIView = transitionContext.viewForKey(UITransitionContextToViewKey)!
transitionContext.containerView()!.addSubview(fromView)
transitionContext.containerView()!.addSubview(toView)
//The "to" view with start "off screen" and slide left pushing the "from" view "off screen"
toView.frame = CGRectMake(toView.frame.width, 0, toView.frame.width, toView.frame.height)
let fromNewFrame = CGRectMake(-1 * fromView.frame.width, 0, fromView.frame.width, fromView.frame.height)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
toView.frame = CGRectMake(0, 0, 320, 560)
fromView.frame = fromNewFrame
}) { (Bool) -> Void in
transitionContext.completeTransition(true)
}
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.35
}
}
This is the view controller code
override func viewDidLoad() {
super.viewDidLoad()
let animatedTransitioningObject = TransitioningObject()
animatedTransitioningObject.animateTransition(UIViewControllerContextTransitionin) //I get an error here
// Do any additional setup after loading the view.
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toViewController.view];
toViewController.view.alpha = 0;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
toViewController.view.alpha = 1;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
Is how your Animation function should look.
Also if You want a pod that does this for you I would suggest checking out this pod
https://github.com/Ramotion/animated-tab-bar
I want to create an animated popover bubble like the one below. With the same expanding/contracting bounce animation. Does anyone know how to go about this? Is is just a UIView with animation? What animation effect is this? Any pointers on how to go about this would be really appreciated. Thanks!
You can use the
Obj-C:
animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:animations completion: method like this:
UIView *animationView = [[UIView alloc] initWithFrame:CGRectMake(50, 90, 100, 50)];
animationView.backgroundColor = [UIColor redColor];
animationView.transform = CGAffineTransformMakeScale(0, 0);
[self.view addSubview:animationView];
[UIView animateWithDuration:0.3
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:5
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
animationView.transform = CGAffineTransformIdentity;
}
completion:nil];
Swift:
var animationView: UIView = UIView(frame: CGRectMake(50, 90, 100, 50))
animationView.backgroundColor = UIColor.redColor()
animationView.transform = CGAffineTransformMakeScale(0, 0)
self.view!.addSubview(animationView)
UIView.animateWithDuration(0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 5, options: .CurveEaseInOut, animations: {() -> Void in
animationView.transform = CGAffineTransformIdentity
}, completion: { _ in })
i think it is UIPopoverPresentationController.
sample:
-(void)showPopover:(UIBarButtonItem*)sender{
if(!_btnController){
_btnController = [[ButtonsViewController alloc] init];
_btnController.delegate = self;
}
if (_popover == nil) {
_btnController.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController *pc = [ _btnController popoverPresentationController];
pc.barButtonItem = sender;
pc.permittedArrowDirections = UIPopoverArrowDirectionAny;
pc.delegate = self;
[self presentViewController: _btnController animated:YES completion:nil];
} else {
//The color picker popover is showing. Hide it.
[_popover dismissPopoverAnimated:YES];
_popover = nil;
}}
I run this code when a swipe event is fired, it shows an arrow zooming in while fading out, as you see I set the location of the image to be the same location as where the swipe is, but it only starts from where the previous touch was(set in the completion block). But at the start of the method I set update the location so why is it animating from the location of the previous touch?
static bool isAnimating;
- (void)animateImageZoom: (UIImageView *)imageView startingAtLocation:(CGPoint)location inDirection: (FadeDirection)direction
{
if (!isAnimating) {
CGRect previousFrame = imageView.frame;
imageView.frame = CGRectMake(location.x, location.y, previousFrame.size.width, previousFrame.size.height);
imageView.hidden = NO;
[UIView animateWithDuration:0.6f
delay:0.3f
options:UIViewAnimationOptionBeginFromCurrentState
animations:^(void) {
isAnimating = YES;
imageView.alpha = 0;
// ZOOM
if (direction == fadeLeft) {
imageView.frame = CGRectMake(location.x,
location.y, 256, 256);
} else if (direction == fadeRight) {
imageView.frame = CGRectMake(location.x,
location.y, 256, 256);
}
}
completion:^(BOOL finished) {
isAnimating = NO;
imageView.frame = CGRectMake(location.x, location.y, previousFrame.size.width, previousFrame.size.height);
imageView.hidden = YES;
imageView.alpha = 0.8f;
}];
}
}
I have a UILabel with background color as grey.
I want a blinking effect on this label like it should become a little white & then become gray and it should keep happen till I turn it off programatically.
Any clue how to achieve this?
You can do this within a block:
self.yourLabel.alpha = 1;
[UIView animateWithDuration:1.5 delay:0.5 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations:^{
self.yourLabel.alpha = 0;
} completion:nil];
So you dont need a second method.
Swift 3
extension UILabel {
func startBlink() {
UIView.animate(withDuration: 0.8,
delay:0.0,
options:[.allowUserInteraction, .curveEaseInOut, .autoreverse, .repeat],
animations: { self.alpha = 0 },
completion: nil)
}
func stopBlink() {
layer.removeAllAnimations()
alpha = 1
}
}
You can simply make an extension to the UILabel class that will support the blinking effect. I don't think using a timer is a right approach since you won't have any fade effect.
Here is the Swift way to do this:
extension UILabel {
func blink() {
self.alpha = 0.0;
UIView.animateWithDuration(0.8, //Time duration you want,
delay: 0.0,
options: [.CurveEaseInOut, .Autoreverse, .Repeat],
animations: { [weak self] in self?.alpha = 1.0 },
completion: { [weak self] _ in self?.alpha = 0.0 })
}
}
Swift 3:
extension UILabel {
func blink() {
self.alpha = 0.0;
UIView.animate(withDuration: 0.8, //Time duration you want,
delay: 0.0,
options: [.curveEaseInOut, .autoreverse, .repeat],
animations: { [weak self] in self?.alpha = 1.0 },
completion: { [weak self] _ in self?.alpha = 0.0 })
}
}
EDIT Swift 3: Works for almost any view
extension UIView {
func blink() {
self.alpha = 0.0;
UIView.animate(withDuration: 0.8, //Time duration you want,
delay: 0.0,
options: [.curveEaseInOut, .autoreverse, .repeat],
animations: { [weak self] in self?.alpha = 1.0 },
completion: { [weak self] _ in self?.alpha = 0.0 })
}
}
Use NSTimer
NSTimer *timer = [NSTimer
scheduledTimerWithTimeInterval:(NSTimeInterval)(1.0)
target:self
selector:#selector(blink)
userInfo:nil
repeats:TRUE];
BOOL blinkStatus = NO;
in your blink function
-(void)blink{
if(blinkStatus == NO){
yourLabel.backgroundColor = [UIColor whiteColor];
blinkStatus = YES;
}else {
yourLabel.backgroundColor = [UIColor grayColor];
blinkStatus = NO;
}
}
A different approach but works. Blinking only for 3 seconds
extension UIView {
func blink() {
let animation = CABasicAnimation(keyPath: "opacity")
animation.isRemovedOnCompletion = false
animation.fromValue = 1
animation.toValue = 0
animation.duration = 0.8
animation.autoreverses = true
animation.repeatCount = 3
animation.beginTime = CACurrentMediaTime() + 0.5
self.layer.add(animation, forKey: nil)
}
}
Rather use the view animations. It makes it very simple and is easy to control. Try this:
self.yourLabel.alpha = 1.0f;
[UIView animateWithDuration:0.12
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut |
UIViewAnimationOptionRepeat |
UIViewAnimationOptionAutoreverse |
UIViewAnimationOptionAllowUserInteraction
animations:^{
self.yourLabel.alpha = 0.0f;
}
completion:^(BOOL finished){
// Do nothing
}];
You can tweak the values to get different effects for example, changing animateWithDuration wil set the blinking speed. Further you can use it on anything that inherits from UIView example a button, label, custom view etc.
Tweaking Krishnabhadra Answer to give a better blink effect
Declare a Class variable bool blinkStatus;
And paste code given below
NSTimer *yourtimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(10.0 / 60.0) target:self selector:#selector(blink) userInfo:nil repeats:TRUE];
blinkStatus = FALSE;
-(void)blink{
if(blinkStatus == FALSE){
yourLabel.hidden=NO;
blinkStatus = TRUE;
}else {
yourLabel.hidden=YES;
blinkStatus = FALSE;
}
}
-(void) startBlinkingLabel:(UILabel *)label
{
label.alpha =1.0f;
[UIView animateWithDuration:0.32
delay:0.0
options: UIViewAnimationOptionAutoreverse |UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionBeginFromCurrentState
animations:^{
label.alpha = 0.0f;
}
completion:^(BOOL finished){
if (finished) {
}
}];
}
-(void) stopBlinkingLabel:(UILabel *)label
{
// REMOVE ANIMATION
[label.layer removeAnimationForKey:#"opacity"];
label.alpha = 1.0f;
}
My swift version based on Flex Elektro Deimling's answer:
private func startTimeBlinkAnimation(start: Bool) {
if start {
timeContainerView.alpha = 1
UIView.animateWithDuration(0.6, delay: 0.3, options:[.Repeat, .Autoreverse], animations: { _ in
self.timeContainerView.alpha = 0
}, completion: nil)
}
else {
timeContainerView.alpha = 1
timeContainerView.layer.removeAllAnimations()
}
}
Got stuck when trying with swift and using multiple options but this seems to work nicely:
self.cursorLabel.alpha = 1
UIView.animate(withDuration: 0.7, delay: 0.0, options: [.repeat, .autoreverse, .curveEaseInOut], animations: {
self.cursorLabel.alpha = 0
}, completion: nil)
This is how it worked for me. I adapted the answer of #flex_elektro_deimling
First parameter UIView.animateWithDuration is the total time of the animation (In my case I've set it to 0.5), you may set different values on the first and second (delay) to change the blinking speed.
self.YOURLABEL.alpha = 0;
UIView.animateWithDuration(
0.5,
delay: 0.2,
options: UIViewAnimationOptions.Repeat | UIViewAnimationOptions.Autoreverse, animations: {
self.YOURLABEL.alpha = 1
},
completion:nil)
int count;
NSTimer *timer;
timer= [NSTimer
scheduledTimerWithTimeInterval:(NSTimeInterval)(0.5)
target:self
selector:#selector(animationStart)
userInfo:nil
repeats:TRUE];
-(void)animationStart{
switch (count) {
case 0:
//205 198 115
count++;
lbl.textColor=[UIColor colorWithRed:205.0f/255.0f green:198.0f/255.0f blue:115.0f/255.0f alpha:1];
break;
case 1:
count++;
//205 198 115 56 142 142
lbl.textColor=[UIColor colorWithRed:56.0f/255.0f green:142.0f/255.0f blue:142.0f/255.0f alpha:1];
break;
case 2:
count++;
//205 198 115
lbl.textColor=[UIColor colorWithRed:205.0f/255.0f green:205.0f/255.0f blue:0.0f/255.0f alpha:1];
break;
case 3:
count++;
//205 198 115 84 255 159
lbl.textColor=[UIColor colorWithRed:84.0f/255.0f green:255.0f/255.0f blue:159.0f/255.0f alpha:1];
break;
case 4:
count++;
//205 198 115 255 193 37
lbl.textColor=[UIColor colorWithRed:255.0f/255.0f green:193.0f/255.0f blue:37.0f/255.0f alpha:1];
break;
case 5:
count++;
//205 198 115 205 200 177
lbl.textColor=[UIColor colorWithRed:205.0f/255.0f green:200.0f/255.0f blue:117.0f/255.0f alpha:1];
break;
case 6:
count++;
//205 198 115 255 228 181
lbl.textColor=[UIColor colorWithRed:255.0f/255.0f green:228.0f/255.0f blue:181.0f/255.0f alpha:1];
break;
case 7:
count++;
//205 198 115 233 150 122
lbl.textColor=[UIColor colorWithRed:233.0f/255.0f green:150.0f/255.0f blue:122.0f/255.0f alpha:1];
break;
case 8:
count++;
//205 198 115 233 150 122
lbl.textColor=[UIColor colorWithRed:255.0f/255.0f green:200.0f/255.0f blue:200.0f/255.0f alpha:1];
break;
case 9:
count=0;
//205 198 115 255 99 71 255 48 48
lbl.textColor=[UIColor colorWithRed:255.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1];
break;
default:
break;
}
}
Here is my solution in Swift 4.0 with extension for any UIVIew
extension UIView{
func blink() {
self.alpha = 0.2
UIView.animate(withDuration: 1,
delay: 0.0,
options: [.curveLinear,
.repeat,
.autoreverse],
animations: { self.alpha = 1.0 },
completion: nil)
}
}
For Swift 3+, building on all the great answers here, I ended up with a few tweaks that gave me smooth blinking effect that automatically stops after a given number of cycles.
extension UIView {
func blink(duration: Double=0.5, repeatCount: Int=2) {
self.alpha = 0.0;
UIView.animate(withDuration: duration,
delay: 0.0,
options: [.curveEaseInOut, .autoreverse, .repeat],
animations: { [weak self] in
UIView.setAnimationRepeatCount(Float(repeatCount) + 0.5)
self?.alpha = 1.0
}
)
}
}