I had the same problem when trying to implement a circular spirit level using CMDeviceMotion. I found that this is a problem with the coordinates passed to atan2(y,x) . This function requires Cartesian coordinates, with (0,0) in the center of the view. However, the screen coordinates are (0,0) in the upper left corner. I created methods for converting a point between two coordinate systems, and now it works well.
I posted an example project here on github, but here is the most important part:
float distance = sqrtf(((point.x - halfOfWidth) * (point.x - halfOfWidth)) + ((point.y - halfOfWidth) * (point.y - halfOfWidth))); if (distance > maxDistance) { // Convert point from screen coordinate system to cartesian coordinate system, // with (0,0) located in the centre of the view CGPoint pointInCartesianCoordSystem = [self convertScreenPointToCartesianCoordSystem:point inFrame:self.view.frame]; // Calculate angle of point in radians from centre of the view CGFloat angle = atan2(pointInCartesianCoordSystem.y, pointInCartesianCoordSystem.x); // Get new point on the edge of the circle point = CGPointMake(cos(angle) * maxDistance, sinf(angle) * maxDistance); // Convert back to screen coordinate system point = [self convertCartesianPointToScreenCoordSystem:point inFrame:self.view.frame]; }
AND:
- (CGPoint)convertScreenPointToCartesianCoordSystem:(CGPoint)point inFrame:(CGRect)frame { float x = point.x - (frame.size.width / 2.0f); float y = (point.y - (frame.size.height / 2.0f)) * -1.0f; return CGPointMake(x, y); } - (CGPoint)convertCartesianPointToScreenCoordSystem:(CGPoint)point inFrame:(CGRect)frame { float x = point.x + (frame.size.width / 2.0f); float y = (point.y * -1.0f) + (frame.size.height / 2.0f); return CGPointMake(x, y); }
source share