5
votes

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?

7

7 Answers

19
votes

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:

demo of rotating

13
votes

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
        }
    })
 }
10
votes

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);
1
votes

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

0
votes

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.

0
votes

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")
}
0
votes

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
        })
    }