Reputation: 37
I used the information from How to rotate an UIImageView using TouchesMoved in my own project. But I found a problem, and I can’t figure out how to solve it.
The view in the original post has the same problem but with the small view it is almost not noticeable. A made this example code. See how the view “jumps”. The touch point is always left of the anchor point. How can I avoid this jump.
class ViewController: UIViewController {
var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
myView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 200))
myView.center = self.view.center
myView.isUserInteractionEnabled = true
myView.backgroundColor = UIColor.red
self.view.addSubview(myView)
myView.layer.anchorPoint = CGPoint(x: 0.5, y: 0.0)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
if touch.view === myView {
let position = touch.location(in: self.view)
let target = myView.center
let angle = atan2(target.y-position.y, target.x-position.x)
myView.transform = CGAffineTransform(rotationAngle: angle)
}
}
}
Upvotes: 0
Views: 936
Reputation: 535306
You have two major problems:
You are not taking into account where the original touch was. You need to record that information in touchesBegan
and work out the angle of your rotation transform with respect to that.
You are saying let target = myView.center
as if this were the point we are rotating around. But it isn't, because you moved the anchorPoint
of myView
.
Here's working code (taken from my book) that lets you drag to rotate myView
around its own center:
class ViewController: UIViewController {
var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
myView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 200))
myView.center = self.view.center
myView.isUserInteractionEnabled = true
myView.backgroundColor = UIColor.red
self.view.addSubview(myView)
}
func pToA (_ t:UITouch) -> CGFloat {
let loc = t.location(in: myView)
let c = myView.convert(myView.center, from:myView.superview!)
return atan2(loc.y - c.y, loc.x - c.x)
}
var initialAngle = CGFloat(0)
var angle = CGFloat(0)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.initialAngle = pToA(touches.first!)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let ang = pToA(touches.first!) - self.initialAngle
let absoluteAngle = self.angle + ang
myView.transform = myView.transform.rotated(by: ang)
self.angle = absoluteAngle
}
}
But if you really want to rotate around a different point (moving the anchor point as your code does), you'd need to adjust that code accordingly.
Upvotes: 3
Reputation: 879
It looks like your issue is in the line
let angle = atan2(target.y-position.y, target.x-position.x)
atan2 returns a number between 0 and π, rather than what you want, a number between -π and π. You should be able to fix it by comparing (target.y - position.y)
with 0. When it is greater than zero, your original returned value should be good. When it is less than zero, you will want to subtract π to get the correct angle.
Upvotes: -1