2

I am trying to rotate a view towards another views center point(Remember not around, its towards).

Assume I have 2 views placed like this

enter image description here

Now I want to rotate the topview to point the bottom view like this

enter image description here

so this what I did

  • Change the top views anchor point to its origin. so that it can rotate and point its edge to the bottom view
  • Calculated the angle between the first views origin point and the bottom views center
  • And applied the calculated transform to the top view.

    Below the code I am using

    let rect = CGRect(x: 70, y: 200, width: 300, height: 100)
    let rectView = UIView(frame: rect)
    rectView.layer.borderColor = UIColor.green.cgColor;
    rectView.layer.borderWidth = 2;
    
    let endView = UIView(frame: CGRect(x: 250, y: 450, width: 70, height: 70))
    endView.layer.borderColor = UIColor.green.cgColor;
    endView.layer.borderWidth = 2;
    let end = endView.center;
    
    self.view.addSubview(endView)
    self.view.addSubview(rectView!)
    rectView.setAnchorPoint(CGPoint.zero)
    
    let angle = rectView.bounds.origin.angle(to: end);
    UIView.animate(withDuration: 3) {
        rectView.transform = rectView.transform.rotated(by: angle)
    }   
    

I am using this extension from Get angle from 2 positions to calculate the angle between 2 points

extension CGPoint {
  func angle(to comparisonPoint: CGPoint) -> CGFloat {
    let originX = comparisonPoint.x - self.x
    let originY = comparisonPoint.y - self.y
    let bearingRadians = atan2f(Float(originY), Float(originX))
    var bearingDegrees = CGFloat(bearingRadians).degrees
    while bearingDegrees < 0 {
        bearingDegrees += 360
    }
    return bearingDegrees
  }
}

extension CGFloat {
    var degrees: CGFloat {
        return self * CGFloat(180.0 / M_PI)
    }
}

extension UIView {
    func setAnchorPoint(_ point: CGPoint) {
        var newPoint = CGPoint(x: bounds.size.width * point.x, y: bounds.size.height * point.y)
        var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y);

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

        var position = layer.position

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

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

        layer.position = position
        layer.anchorPoint = point
    }
}

But this isn't working as expected, the rotation is way off. Check the below screen capture of the issue

enter image description here

I assume this issue is related to how the angle is calculated, but I could't figure out what?

any help is much appreciated.

RameshVel
  • 64,778
  • 30
  • 169
  • 213

1 Answers1

2

Angles need to be in radians

The rotation angle needs to be specified in radians:

Change:

rectView.transform = rectView.transform.rotated(by: angle)

to:

rectView.transform = rectView.transform.rotated(by: angle / 180.0 * .pi)

or change your angle(to:) method to return radians instead of degrees.


Use frame instead of bounds

Also, you need to use the frame of your rectView when computing the angle. The bounds of a view is its internal coordinate space, which means its origin is always (0, 0). You want the frame which is the coordinates of the view in its parent's coordinate system.

Change:

let angle = rectView.bounds.origin.angle(to: end)

to:

let angle = rectView.frame.origin.angle(to: end)

Note: Because your anchorPoint is the corner of rectView, this will point the top edge of rectView to the center of endView. One way to fix that would be to change your anchorPoint to the center of the left edge of rectView and then use that point to compute your angle.

vacawama
  • 150,663
  • 30
  • 266
  • 294