Rotate Uiview Around Its Center Keeping Its Size

Rotate UIView around its center keeping its size

You're probably hitting a problem with Autolayout. You probably have constraints on the rotated view pinning it to the edges of the superview. When the transform is applied, Autolayout is updating the view's size to still fit within the superview.

You can experiment with different constraints (e.g. pinning the centre of the view to the centre of another view, and pinning the width and height to constant values) or turn Autolayout off for the rotated view, or, if these don't work or don't suit your needs, use a container view which is laid out under Autolayout, and add your rotating view to this, without using Autolayout.

This can only be done in code - you can make individual views subject to Autolayout or not by setting translatesAutoresizingMasksIntoConstraints to NO (Autolayout on) or YES (Autolayout off). You'll need to set the appropriate autoresizing masks if you switch a view from one to the other.

How to rotate a UIView around it's proper center without distortion / skew

The problem was that I was setting the transform more than once per animation frame. This was causing a compound effect with different values. You need to organise your variables so that you only set the transform once during the property modifications.

Changing UIView rotation will change it's frame size. How to keep frame size after rotating?

You need the scaled but non-rotated view size. You can compute that from the bounds.size of the view and the transform.

Example

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 150))

// Scale view by 3X horizontally and by 4X vertically and
// rotate by 45 degrees
view.transform = CGAffineTransform(scaleX: 3, y: 4).rotated(by: 45.0 / 180.0 * .pi)

let transform = view.transform

// Find the angle, print it in degrees
let angle = atan2(-transform.c, transform.a)
print(angle * 180.0 / .pi) // 44.99999999999999

// Find scaleX and scaleY
let scaleX = transform.a * cos(angle) - transform.c * sin(angle)
let scaleY = transform.d * cos(angle) + transform.b * sin(angle)
print(scaleX) // 3.0
print(scaleY) // 4.0

print(view.frame.size) // (530.3300858899106, 707.1067811865476)
print(view.bounds.size) // (100.0, 150.0)

let adjustedSize = CGSize(width: view.bounds.size.width * scaleX, height: view.bounds.size.height * scaleY)

// print non-rotated but scaled size
print(adjustedSize) // (300.0, 600.0)

Here it is as a handy extension that will work for views that are scaled, rotated, and translated (moved):

extension CGAffineTransform {
var angle: CGFloat { return atan2(-self.c, self.a) }

var angleInDegrees: CGFloat { return self.angle * 180 / .pi }

var scaleX: CGFloat {
let angle = self.angle
return self.a * cos(angle) - self.c * sin(angle)
}

var scaleY: CGFloat {
let angle = self.angle
return self.d * cos(angle) + self.b * sin(angle)
}
}

Now, if you have a UIView:

let angle = view.transform.angleInDegrees
let scaleX = view.transform.scaleX
let scaleY = view.transform.scaleY
let adjustedSize = CGSize(width: view.bounds.size.width * scaleX, height: view.bounds.size.height * scaleY)

How to rotate UIVIew in its top left coordinates?

Thanks Here Trix and the4kman for the idea. I solved my problem. Here Trix answer not fully helped me. But He shows some hints. Using that hints I solved my answer.

Step 1

Create this extension for UIView to set anchorPoint

extension UIView{
func setAnchorPoint(anchorPoint: CGPoint) {

var newPoint = CGPoint(x: self.bounds.size.width * anchorPoint.x, y: self.bounds.size.height * anchorPoint.y)
var oldPoint = CGPoint(x: self.bounds.size.width * self.layer.anchorPoint.x, y: self.bounds.size.height * self.layer.anchorPoint.y)

newPoint = newPoint.applying(self.transform)
oldPoint = oldPoint.applying(self.transform)

var position : CGPoint = self.layer.position

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

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

self.layer.position = position;
self.layer.anchorPoint = anchorPoint;
}
}

Usage

rectangle.setAnchorPoint(anchorPoint: CGPoint.zero)
rectangle.transform = CGAffineTransform(rotationAngle: 90.degreesToRadians)

This working perfectly.

Rotate UIImageView around its center

Answer below:

It looks like your image asset isn’t centred properly. Set the background colour of your image view so you can tell. The image view is probably rotating about its center, but the image content itself is probably off center. - Peter Parker

Basically your image of the rings does not have the rings smack in the middle. So you need to put that image into photoshop and export a new one such that the rings are smack in the middle.
Peter Parker

Thanks Peter Parker

Rotate object around point when it's initially rotated by it's center

Finally, I've found a solution. Instead of calculating rr I've replaced 3-step rotation around point with 1-step method:

let tr = CGAffineTransform(
a: cos(angle),
b: sin(angle),
c: -sin(angle),
d: cos(angle),
tx: a.x - a.x * cos(angle) + a.y * sin(angle),
ty: a.y - a.x * sin(angle) - a.y * cos(angle)
)

self.orbitingImageView.transform = self.orbitingImageView.transform
.concatenating(tr)

Whole code:

import UIKit

class ViewController: UIViewController {

var orbitingImageView: UIImageView!
var centerImageView: UIImageView!

let rr: CGFloat = .pi / 4

override func viewDidLoad() {
super.viewDidLoad()

centerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
centerImageView.center = CGPoint(x: 300, y: 400)
centerImageView.backgroundColor = .red
view.addSubview(centerImageView)

orbitingImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
orbitingImageView.center = CGPoint(x: 500, y: 400)
orbitingImageView.backgroundColor = .blue
view.addSubview(orbitingImageView)

orbitingImageView.transform = orbitingImageView.transform
.rotated(by: rr)
.scaledBy(x: 0.5, y: 0.5) //even works with prescaling

n()
}

func n() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {

let angle: CGFloat = 0.05

let orb = self.orbitingImageView!
let cen = self.centerImageView!

let a = CGPoint(x: cen.center.x - orb.center.x,
y: cen.center.y - orb.center.y)

let tr = CGAffineTransform(
a: cos(angle),
b: sin(angle),
c: -sin(angle),
d: cos(angle),
tx: a.x - a.x * cos(angle) + a.y * sin(angle),
ty: a.y - a.x * sin(angle) - a.y * cos(angle)
)

self.orbitingImageView.transform = self.orbitingImageView.transform
.concatenating(tr)

self.n()
}
}

}

Now it works with pre-rotation as well as with pre-scaling.

Rotating a UIView around a center point without rotating the view itself

This is pretty easy to do in Core Animation. I've used some pretty boring static values for this example, so obviously you'll want to make some modifications. But this will show you how to move a view along a circular UIBezierPath.

UIView *view = [UIView new];
[view setBackgroundColor:[UIColor redColor]];
[view setBounds:CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)];
[view setCenter:[self pointAroundCircumferenceFromCenter:self.view.center withRadius:140.0f andAngle:0.0f]];
[self.view addSubview:view];

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(CGRectGetMidX(self.view.frame) - 140.0f, CGRectGetMidY(self.view.frame) - 140.0f, 280.0f, 280.0f)];

CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.duration = 5.0;

pathAnimation.path = [path CGPath];

[view.layer addAnimation:pathAnimation forKey:@"curveAnimation"];

And a basic function to generate the initial center of the view.

- (CGPoint)pointAroundCircumferenceFromCenter:(CGPoint)center withRadius:(CGFloat)radius andAngle:(CGFloat)theta
{
CGPoint point = CGPointZero;
point.x = center.x + radius * cosf(theta);
point.y = center.y + radius * sinf(theta);

return point;
}

transform view, keep subview size

The actual solution:

if ([recognizer respondsToSelector:@selector(rotation)]) {
CGFloat rotation = 0.0f - (self.lastRotation - [(UIRotationGestureRecognizer *) recognizer rotation]);

CGAffineTransform currentTransform = self.transform;
CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform, rotation);
self.transform = newTransform;

self.lastRotation = [(UIRotationGestureRecognizer *) recognizer rotation];

} else if ([recognizer respondsToSelector:@selector(scale)]) {
CGFloat scale = 1.0f - (self.lastScale - [(UIPinchGestureRecognizer *) recognizer scale]);

CGRect bounds = self.bounds;
bounds.size.width *= scale;
bounds.size.height *= scale;
self.bounds = bounds;

self.lastScale = [(UIPinchGestureRecognizer *) recognizer scale];
}


Related Topics



Leave a reply



Submit