0
votes

I have a segmented control and I'd like to animate a UIView to rotate clockwise when the right side of the control is tapped, and counter-clockwise when the left side is tapped. My code to rotate is:

func rotateRight() {
    UIView.animate(withDuration: 0.5, animations: {
        self.profileImageView.transform = CGAffineTransform(rotationAngle: (180.0 * CGFloat(M_PI)) / 180.0)
        self.profileImageView.transform = CGAffineTransform(rotationAngle: (0.0 * CGFloat(M_PI)) / 360.0)
    })

And then I'm using this code to handle the changes depending on which segment is tapped (Edited for update):

    if loginRegisterSegmentedControl.selectedSegmentIndex == 0 {
        self.loginRegisterButton.backgroundColor = UIColor.blue
        loginRegisterSegmentedControl.tintColor = UIColor.blue
        rotateLeft()
        // Roll to the left
    } else {
        self.loginRegisterButton.backgroundColor = UIColor.red
        loginRegisterSegmentedControl.tintColor = UIColor.red
        rotateRight()
        // Roll to the right
    }

So basically in the if conditional I want to call a rotateLeft() function - how can I do this?

EDIT: Here's my full code for the animation, including Pierce's suggestion:

// Rotation
var viewAngle: CGFloat = 0    // Right-side up to start
let π = CGFloat.pi   // Swift allows special characters hold alt+p to use this, it allows for cleaner code

func rotate(by angle: CGFloat) {

    self.viewAngle += angle

    UIView.animate(withDuration: 0.5, animations: {
        self.profileImageView.transform = CGAffineTransform(rotationAngle: self.viewAngle)
        self.view.layoutIfNeeded()
    })         

}

lazy var profileImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.image = UIImage(named: "TTTdude")
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFill

    imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(rotate)))
    imageView.isUserInteractionEnabled = true

    return imageView
}()



lazy var loginRegisterSegmentedControl: UISegmentedControl = {
    let sc = UISegmentedControl(items: ["iOS", "Android"])
    sc.translatesAutoresizingMaskIntoConstraints = false
    sc.tintColor = UIColor.black
    sc.selectedSegmentIndex = 0
    sc.addTarget(self, action: #selector(handleLoginRegisterChange), for: .valueChanged)
    return sc
}()

func handleLoginRegisterChange() {

    let title = loginRegisterSegmentedControl.titleForSegment(at: loginRegisterSegmentedControl.selectedSegmentIndex)
    loginRegisterButton.setTitle(title, for: .normal)

    if loginRegisterSegmentedControl.selectedSegmentIndex == 0 {
        self.loginRegisterButton.backgroundColor = UIColor.blue
        loginRegisterSegmentedControl.tintColor = UIColor.blue
        rotate(by: -2*π)
        // Roll to the left
    } else {
        self.loginRegisterButton.backgroundColor = UIColor.red
        loginRegisterSegmentedControl.tintColor = UIColor.red
        rotate(by: 2*π)
        // Roll to the right
    }
3

3 Answers

4
votes

Just rotate it by whatever angle you did for clockwise, but multiply it by negative one

func rotateLeft() {
    UIView.animate(withDuration: 0.5, animations: {
        self.profileImageView.transform = CGAffineTransform(rotationAngle: ((180.0 * CGFloat(M_PI)) / 180.0) * -1)
        self.profileImageView.transform = CGAffineTransform(rotationAngle: ((0.0 * CGFloat(M_PI)) / 360.0) * -1)
        self.view.layoutIfNeeded()
    })
}

I should also mention that when you say (180.0 * CGFloat(π)) / 180 - You're just saying π, because 180/180 is one.

It looks to me like you're trying to do two different rotations at once? I'm a little confused by that. One thing I've found that really helps is to track the view's axis orientation if you're doing multiple rotations both clockwise and counter-clockwise. Do something like create a property called viewAngle

var viewAngle: CGFloat = 0    // Right-side up to start
let π = CGFloat.pi   // Swift allows special characters hold alt+p to use this, it allows for cleaner code

func rotate(by angle: CGFloat) {

    for i in 0 ..< 4 {
        UIView.animate(withDuration: 0.125, delay: 0.125 * Double(i), options: .curveLinear, animations: {

            self.viewAngle += angle/4
            self.rotateView.transform = CGAffineTransform(rotationAngle: self.viewAngle)
            self.view.layoutIfNeeded()
        }, completion: nil)
    }        

}

If you want to rotate right pass in a positive angle (i.e. π, π/2, π/4, 2*π), or if left pass in a negative angle.

Rotate right by 180 degrees:

rotate(by: π)

Rotate left by 180 degrees:

rotate(by: -π)

EDIT: As you were mentioning, which I didn't see until I tried for myself, when you plug in π or 2π whether it's negative or positive, it just rotates clockwise. To get around this I had to make it rotate by -π/4 increments. So to do a full-circle rotate counter clockwise I did a loop of -π/4 animations if that makes sense. It works, but I feel like there should be an easier way to do this. I would appreciate anyone else chiming in.

0
votes

(swift 4)

it will rotate 180 degree counter clock wise

 self.viewRoatate.transform = CGAffineTransform(rotationAngle:  CGFloat.pi / -2)
            self.viewRoatate.transform = CGAffineTransform(rotationAngle:  CGFloat.pi)
self.view.layoutIfNeeded()
0
votes
func rotate(button: UIButton, time: Double, overlap: Double, clockWise: Bool) {
var angles = [CGFloat(Double.pi), CGFloat(Double.pi) * 2]
if !clockWise {
    angles = [CGFloat(Double(270) * .pi/180),
              CGFloat(Double(180) * .pi/180),
              CGFloat(Double(90) * .pi/180),
              CGFloat(Double(0) * .pi/180)]
}
for i in 0..<angles.count {
    UIView.animate(withDuration: time, delay: time - overlap * Double(i), animations: {
        button.transform = CGAffineTransform(rotationAngle: angles[i])
    }, completion: nil)
}