Rotate a Imageview Around a Pivot Point in iOS

Rotate a ImageView around a pivot point in iOS

Answer is having both ways to do so using a PanGesture and automatically using a Timer.

1. Using UIPanGestureRecognizer:

You can achieve this using UIPanGestureRecognizer.
circleView is your mainView and nob is another view or imageView which will move outside the circleView

panGesture = UIPanGestureRecognizer(target: self, action: #selector(panHandler(_:)))
nob.addGestureRecognizer(panGesture)

Definition of panHandler(_ :)

@objc func panHandler(_ gesture: UIPanGestureRecognizer) {
let point = gesture.location(in: self)
updateForPoints(point)
}

And here is the core logic how will it work.

func updateForPoints(_ point: CGPoint) {

/*
* Parametric equation of circle
* x = a + r cos t
* y = b + r sin ⁡t
* a, b are center of circle
* t (theta) is angle
* x and y will be points which are on circumference of circle
*
270
|
_ | _
|
180 -------o------- 360
|
+ | +
|
90
*
*/

let centerOffset = CGPoint(x: point.x - circleView.frame.midX, y: point.y - circleView.frame.midY)

let a: CGFloat = circleView.center.x
let b: CGFloat = circleView.center.y
let r: CGFloat = circleView.layer.cornerRadius - 2.5
let theta: CGFloat = atan2(centerOffset.y, centerOffset.x)
let newPoints = CGPoint(x: a + r * cos(theta), y: b + r * sin(theta))

var rect = nob.frame
rect.origin = newPoints
nob.center = newPoints
}

2. Automatically move using Timer

let totalSeconds: Int = 30 // You can change it whatever you want
var currentSecond: Int = 1
var timer: Timer?

func degreesToRadians(_ degree: CGFloat) -> CGFloat {
/// Will convert the degree (180°) to radians (3.14)
return degree * .pi / 180
}

func angleFromSeconds(_ seconds: Int) -> CGFloat {
/// Will calculate the angle for given seconds
let aSliceAngle = 360.0 / CGFloat(totalSeconds)
let angle = aSliceAngle * CGFloat(seconds) - 90
return angle
}

/// ------------- TIMER METHODS ------------- ///

func startTimer() {
stopTimer()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timeDidUpdate(_ :)), userInfo: nil, repeats: true)
timeDidUpdate(timer!)
}

func stopTimer() {
currentSecond = 1
timer?.invalidate()
timer = nil
}

@objc func timeDidUpdate(_ t: Timer) {
let angle = angleFromSeconds(currentSecond)
let theta = degreesToRadians(angle)
updateAngle(theta: theta, animated: true)
currentSecond += 1

if currentSecond > totalSeconds {
self.stopTimer()
}
}

/// --------------- MAIN METHOD -------------- ///
func updateAngle(theta: CGFloat, animated: Bool) {

let a: CGFloat = circleView.center.x
let b: CGFloat = circleView.center.y
let r: CGFloat = circleView.layer.cornerRadius
let newPoints = CGPoint(x: a + r * cos(theta), y: b + r * sin(theta))

var rect = nob.frame
rect.origin = newPoints

if animated {
UIView.animate(withDuration: 0.1, animations: {
self.nob.center = newPoints

/// Uncomment if your nob is not a circle and you want to maintain the angle too
// self.nob.transform = CGAffineTransform.identity.rotated(by: theta)
})
}
else {
nob.center = newPoints

/// Uncomment if your nob is not a circle and you want to maintain the angle too
//nob.transform = CGAffineTransform.identity.rotated(by: theta)
}
}

rotate UIImageView around an arbitrary point

You can make the image a subview of another view and then rotate the superview to get that effect. Another approach is to set the anchorPoint property as described in the docs.

Rotating a ImageView having one end fixed in iOS

Responding to my comment, OP said the issue was that the control was not rotating around the desired axis. OP was not (I think) clear about what the desired axis is, nor about what the current axis is (I am using axis interchangeably to mean pivot point). There are a few potential fixes.

1) This needle thing is an ImageView, so you could do a simple hack and just make your image bigger or smaller to fix the rotation. To explain: if your needle is rotating around the center of the needle and not the ball part, then simply double the width of the image, such that the needle only takes up the right side and thus the "ball" of the needle is the pivot point.

2) Set the anchor property of the transform to wherever you want it to be rotating around. See this similar SO question & answer.

How can I reliably rotate an image around a point?

This line:

CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);

moves the image by x, y.

(Note-- you will spin around the control point, whatever you've set that to be)

this line:

transform = CGAffineTransformRotate(transform, a);

rotates it around the control point.

If your control point is in the top left hand corner (the default), it will spin around the top lefthand corner.

You need to set this:

[self layer].anchorPoint = CGPointMake(0.5, 0.5);

to get it to spin around the center of the layer.

Setting a rotation point for CGAffineTransformMakeRotation Swift

What you are looking for is the anchorPoint property of your views' layers. This property basically represents the handle that is be used while the view is being moved around.

It defaults to the center of your view so and that why you are always seeing your view rotating form the middle.

You can use this great method i've found here on SO to change the anchorPoint of you views to the top (use CGPointMake(0, 0.5f))

// Swift version
// Place this before you set the transform property
setAnchorPoint(CGPointMake(0, 0.5), view: imageOne)
setAnchorPoint(CGPointMake(0, 0.5), view: imageTwo)
setAnchorPoint(CGPointMake(0, 0.5), view: imageThree)

func setAnchorPoint(anchorPoint: CGPoint, view: UIView) {
var newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y)
var oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y)

newPoint = CGPointApplyAffineTransform(newPoint, view.transform)
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform)

var position : CGPoint = view.layer.position

position.x -= oldPoint.x
position.x += newPoint.x;

position.y -= oldPoint.y;
position.y += newPoint.y;

view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}

This is the objective c version of the same code

// Obj-c version
// Place this before you set the transform property
[self setAnchorPoint:CGPointMake(0, 0.5f) forView:imageOne];
[self setAnchorPoint:CGPointMake(0, 0.5f) forView:imageTwo];
[self setAnchorPoint:CGPointMake(0, 0.5f) forView:imageThree];

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
view.bounds.size.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
view.bounds.size.height * view.layer.anchorPoint.y);

newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

CGPoint position = view.layer.position;

position.x -= oldPoint.x;
position.x += newPoint.x;

position.y -= oldPoint.y;
position.y += newPoint.y;

view.layer.position = position;
view.layer.anchorPoint = anchorPoint;
}

I'd encourage you to try to understand yourself what this code is doing once you have seen what the anchorPoint property represents. (the method is by user: Anand K)

How to rotate image in Swift?

This is an extension of UIImage that targets Swift 4.0 and can rotate just the image without the need for a UIImageView. Tested successfully that the image was rotated, and not just had its exif data changed.

import UIKit

extension UIImage {
func rotate(radians: CGFloat) -> UIImage {
let rotatedSize = CGRect(origin: .zero, size: size)
.applying(CGAffineTransform(rotationAngle: CGFloat(radians)))
.integral.size
UIGraphicsBeginImageContext(rotatedSize)
if let context = UIGraphicsGetCurrentContext() {
let origin = CGPoint(x: rotatedSize.width / 2.0,
y: rotatedSize.height / 2.0)
context.translateBy(x: origin.x, y: origin.y)
context.rotate(by: radians)
draw(in: CGRect(x: -origin.y, y: -origin.x,
width: size.width, height: size.height))
let rotatedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

return rotatedImage ?? self
}

return self
}
}

To perform a 180 degree rotation, you can call it like this:

let rotatedImage = image.rotate(radians: .pi)

If for whatever reason it fails to rotate, the original image will then be returned.



Related Topics



Leave a reply



Submit