Rotate the image by 180 degrees - ios

I have one image like drop down. Initially it will look like drop down image So when user press drop down list some option will show. So what I need is, when drop-down is true.I means when user press drop down image and when the list of option are showing down I need to show the drop down image to 180 degree.Same like when drop down is false I need to show the image as normal position.
Is this way is correct instead of using one more image? I am using swift 2.2
Updated :
#IBAction func dropBtnPress(sender: AnyObject) {
if dropDown.hidden {
dropDown.show()
UIView.animateWithDuration(0.0, animations: {
self.image.transform = CGAffineTransformMakeRotation((180.0 * CGFloat(M_PI)) / 180.0)
})
} else {
dropDown.hide()
// UIView.animateWithDuration(2.0, animations: {
// self.image.transform = CGAffineTransformMakeRotation((180.0 * CGFloat(M_PI)) / -180.0)
// })
}
}
}

To rotate an image you could use this snippet:
UIView.animateWithDuration(2, animations: {
self.imageView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
})
You can adjust the animation seconds (currently 2).
To set the image back again use the following code:
UIView.animateWithDuration(2, animations: {
self.imageV.transform = CGAffineTransform.identity
})
Swift 4.x version:
Rotate:
UIView.animate(withDuration: 2) {
self.imageView.transform = CGAffineTransform(rotationAngle: .pi)
}
Set the image to it´s normal state:
UIView.animate(withDuration: 2) {
self.imageView.transform = CGAffineTransform.identity
}

Swift 3
UIView.animate(withDuration:0.1, animations: {
self.arrowIcon.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
})

First of all, if you want to rotate 180 degrees, that has to translate to radians. 180 degrees in radians is pi. 360 degrees would be 2 * pi. 180 * pi would make your animation spin around 90 times in one second and end up with the original orientation.
You can do something like this,
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = M_PI
rotationAnimation.duration = 1.0
self.arrowImageView.layer.addAnimation(rotationAnimation, forKey: nil)
hope this will help :)

if you want to rotate image to fit language direction when changing language you can make your image assets direction (Left to Right,Mirrors) it works perfect with me from English to arabic You can see it how to do it here

Related

Continuous rotation is not centered

Using this code:
UIView.animate(withDuration: 0.5, delay: 0, options: [.repeat], animations: {
self.star.transform = self.star.transform.rotated(by: CGFloat(M_PI_2))
})
My view is doing this:
Using this code:
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 3) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI * 2)
rotateAnimation.isRemovedOnCompletion = false
rotateAnimation.duration = duration
rotateAnimation.repeatCount=Float.infinity
self.layer.add(rotateAnimation, forKey: nil)
}
}
My view is doing this:
Well both are not doing what I want. The view that is rotating is an UIImageView with scale to fill. I want the image to stay exactly in the middle. How can I accomplish that? The functions are executed in viewDidAppear. The last gif looks way better, but notice the star is not perfectly centered... This is the image.
Problem
The center of your image is not the center of your star.
Solutions
There are two possible solutions
Edit the image so that center of the star is at the center of the image.
Set the anchor of the rotating layer to the center of the star (x: 1004px, y: 761px).

How to make image spin (animation)

I have an image which is a square and I know how to make it spin. But not sure how to make it spin exactly like this animation here:
Notice how it spins... then stops a little... then spins again... and so on.
What I have just is a basic spin but doesn't look like the gif above:
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 3) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI * 2)
rotateAnimation.isRemovedOnCompletion = false
rotateAnimation.duration = duration
rotateAnimation.repeatCount=Float.infinity
self.layer.add(rotateAnimation, forKey: nil)
}
}
Any thoughts on how this is done?
You need to use a timing function that accelerates and decelerates, also known as an ease-in-ease-out timing function.
I've modified your function to use Core Animation's standard ease-in-ease-out timing function:
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 0.8) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
let radians = CGFloat.pi / 4
rotateAnimation.fromValue = radians
rotateAnimation.toValue = radians + .pi
rotateAnimation.isRemovedOnCompletion = false
rotateAnimation.duration = duration
rotateAnimation.repeatCount=Float.infinity
self.layer.add(rotateAnimation, forKey: nil)
}
}
Result:
Note that in your image, it looks like the box pauses every 180 degrees, so I've changed the function to rotate by only 180 degrees. Since the box has 90 degree radial symmetry, it still looks like it goes all the way around, with pauses at 180 and 360 degrees.
If you need to animate an image without any radial symmetry, you'll need to use a CAKeyframeAnimation to achieve the ease-in-ease-out at both 180 and 360 degrees.
Similar effect can also be achieved with a repeated animation with a delay.
UIView.animate(withDuration: 1,
delay: 0.2,
options: [.repeat],
animations: { imageView.transform = imageView.transform.rotated(by: CGFloat.pi) },
completion: nil)

How to animate to rotate counter-clockwise

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
}
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.
(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()
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)
}
By using CABasicAnimation:
left-rotation(counter-clockwise: -360) & right-rotation(clockwise: 360)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.toValue = direction == .left ? -2 * (CGFloat.pi) : 2 * CGFloat.pi
animation.duration = 1
button.layer.add(animation, forKey: nil)

Translate transformation applied while only scale transformation added

I am creating an animation that I want to use when the app is retrieving some data online. The idea is that I have some dots in a row, they will be scaled smaller that their original sizes then return to their original size and all of this with a small delay between each scaling. The animation is repeated and use auto-reverse mode.
To do that I create some dots using a core graphic method, add them to a view and position them using a CGAffineTransformMakeTranslation transformation. Then I use a loop to animate them one by one with a delay and I use a CGAffineTransformScale transformation for scaling.
Problem: I don't get the expected animation (at least what I'm expecting). When the dots are being scaled, they also move back to their original position.
Can someone enlighten me why there is a translate transformation while in the UIView animation, I'm only specifying a scaling?
Here is the code:
private var dots = [UIImage]()
public init(numberOfDots: Int, hexaColor: Int, dotDiameter: CGFloat = 30, animationDuration: NSTimeInterval = 1) {
self.dotDiameter = dotDiameter
self.animationDuration = animationDuration
for _ in 0 ..< numberOfDots {
dots.append(GraphicHelper.drawDisk(hexaColor, rectForDisk: CGRect(x: 0, y: 0, width: dotDiameter, height: dotDiameter), withStroke: false))
}
let spaceBetweenDisks: CGFloat = dotDiameter / 3
let viewWidth: CGFloat = CGFloat(numberOfDots) * dotDiameter + CGFloat(numberOfDots - 1) * spaceBetweenDisks
super.init(frame: CGRectMake(0, 0, viewWidth, dotDiameter))
setup()
}
private func setup() {
for (i, dot) in dots.enumerate() {
let dotImageView = UIImageView(image: dot)
addSubview(dotImageView)
let spaceBetweenDisks: CGFloat = dotDiameter / 3
let xOffset = CGFloat(i) * (dotDiameter + spaceBetweenDisks)
dotImageView.transform = CGAffineTransformMakeTranslation(xOffset, 0)
}
}
public func startAnimation() {
for i in 0 ..< self.dots.count {
let dotImageView: UIImageView = self.subviews[i] as! UIImageView
let transformBeforeAnimation = dotImageView.transform
let delay: NSTimeInterval = NSTimeInterval(i)/NSTimeInterval(self.dots.count) * animationDuration
UIView.animateWithDuration(animationDuration, delay: delay, options: [UIViewAnimationOptions.Repeat, UIViewAnimationOptions.Autoreverse], animations: {
dotImageView.transform = CGAffineTransformScale(dotImageView.transform, 0.05, 0.05)
}, completion: { finished in
dotImageView.transform = CGAffineTransformIdentity
dotImageView.transform = transformBeforeAnimation
})
}
}
EDIT:
I found a fix but I don't understand how come it's fixing it. So if anyone can explain.
I added these 2 lines:
dotImageView.transform = CGAffineTransformIdentity
dotImageView.transform = transformBeforeAnimation
before this line in startAnimation:
dotImageView.transform = CGAffineTransformScale(dotImageView.transform, 0.05, 0.05)
Combining translate and scale transforms is confusing and hard to get right.
I have to spend far too much time with graph paper and deep thought in order to figure it out, and I'm too tired for that right now.
Don't do that. Place your dot image views by moving their center coordinates, and leave the transform at identity. Then when you scale them they should scale in place like you want.
Note that if you want them to move and scale at the same time you can both alter the view's center property and it's transform scale in the same animateWithDuration call and it works correctly. (Not so with changing the frame by the way. If you change the transform then the frame property doesn't work correctly any more. Apple's docs say that the results of reading/writing the frame property of a view with a non-identity transform are "undefined".)
Are you sure its going back to its original position and not scaling based on the original center point instead? Try changing the order of applying transforms by doing this:
public func startAnimation() {
for i in 0 ..< self.dots.count {
let dotImageView: UIImageView = self.subviews[i] as! UIImageView
let transformBeforeAnimation = dotImageView.transform
let delay: NSTimeInterval = NSTimeInterval(i)/NSTimeInterval(self.dots.count) * animationDuration
UIView.animateWithDuration(animationDuration, delay: delay, options: [UIViewAnimationOptions.Repeat, UIViewAnimationOptions.Autoreverse], animations: {
// make scale transform separately and concat the two
let scaleTransform = CGAffineTransformMakeScale(0.05, 0.05)
dotImageView.transform = CGAffineTransformConcat(transformBeforeAnimation, scaleTransform)
}, completion: { finished in
dotImageView.transform = CGAffineTransformIdentity
dotImageView.transform = transformBeforeAnimation
})
}
}
From apple docs:
Note that matrix operations are not commutative—the order in which you concatenate matrices is important. That is, the result of multiplying matrix t1 by matrix t2 does not necessarily equal the result of multiplying matrix t2 by matrix t1.
So, keep in mind that assigning a transformation creates a new affine transformation matrix, and concatenation will modify the existing matrix with the new one - the order you apply these in can create different results.
To make this work, I also updated the value of your translation on dotImageView. It needs to be requiredTranslation / scale.. if applying the translation before the scale. So in your viewDidLoad:
dotImageViewtransform = CGAffineTransformMakeTranslation(1000, 0)
And then the animation:
// make scale transform separately and concat the two
let scaleTransform = CGAffineTransformMakeScale(0.05, 0.05)
self.card.transform = CGAffineTransformConcat(transformBeforeAnimation, scaleTransform)

Funky behavior when animating rotation of UIImageView

I'm trying to rotate an UIImageView with the following code:
var x = -((self.needleImage.frame.size.width/2) - 15) //x for rotation point
UIView.animateWithDuration(5.0, delay: 10.0, options: nil, animations: {
var transform = CGAffineTransformMakeTranslation(x, 0)
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
transform = CGAffineTransformTranslate(transform, -x, 0)
self.needleImage.transform = transformm
The end position is right, but during the animation/rotation, the image is shifted a little to the left before settling at the right place.
I tried the same code with a UIView from this and it doesn't do that.
Then I tried wrapping the Imageview inside a View and rotating that, but that didn't help either.
I have drawn a cicle ontop of the rotation point to check if it isn't in the right place, but it seems alright.
I've used this code snippet in the past to rotate a view perpetually, but by removing the last closure it should work to rotate an image 360 degrees. This can be adjusted, or course. Right now it's set to 2PI. You'll have to convert your rotation angle to radians.
func animateRotator() {
UIView.animateWithDuration(0.5, delay: 0, options: .CurveLinear, animations: { () -> Void in
self.rotator.transform = CGAffineTransformRotate(self.rotator.transform, CGFloat(M_PI_2))
}) { (finished) -> Void in
self.animateRotator()
}
}

Resources