UIView touch location coordinates - uiview

What are the coordinates used in UIViews and their corresponding superviews? I have this code which i would like to detect a 'corridor' where the user can touch... similar to this image:alt text http://img17.imageshack.us/img17/4416/bildschirmfoto20100721u.png
This is the code i have:
CGPoint touch = [recognizer locationInView:[shuttle superview]];
CGPoint centre = shuttle.center;
int outerRadius = shuttle.bounds.size.width/2;
int innerRadius = (shuttle.bounds.size.width/2) - 30;
if ((touch.x < outerRadius && touch.y <outerRadius)){
NSLog(#"in outer");
if(touch.x > innerRadius && touch.y > innerRadius) {
NSLog(#"in corridor");
}
}
The radii are approximately 500 and 600, and the touch x and y are 100 and 200...
Thus, the NSLog "in corridor" never gets called.
Thanks

Your condition is wrong. The corridor according to it is a square, with its center at (0, 0) instead of shuttle.center. Try
CGFloat dx = touch.x - centre.x;
CGFloat dy = touch.y - centre.y;
CGFloat r2 = dx*dx + dy*dy;
if (r2 < outerRadius*outerRadius) {
NSLog(#"in outer");
if (r2 > innerRadius*innerRadius)
NSLog(#"in corridor")
}
instead.
Even if the corridor is indeed expected to be a square, you should check with fabs(dx), fabs(dy) not touch.x, touch.y.

Related

UITapGesture to drag view but limiting the bounds of which it can be dragged

I currently have 2 circles. One big circle and one little circle. The little circle has a tap gesture recognizer that allows it to be dragged by the user. I would like the little circle's center to go no further than the big circle's radius. I have 4 auto layout constraints on the inner circle. 1 for fixed width, 1 for fixed height, 1 for distance from center for x, and 1 for distance from center for y. Here is how I am going about this:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:self.view];
CGFloat x = recognizer.view.center.x + translation.x;
CGFloat y = recognizer.view.center.y + translation.y;
CGPoint desiredPoint = CGPointMake(x, y);
//check if point the user is trying to get to is outside the radius of the outer circle
//if it is, set the center of the inner circle to the right position at the distance of the radius and with the same angle
if ([self distanceBetweenStartPoint:self.outerCircleView.center endPoint:desiredPoint] > self.outerCircleRadius) {
CGFloat angle = [self angleBetweenStartPoint:self.outerCircleView.center endPoint:actualPosition];
desiredPoint = [self findPointFromRadius:self.outerCircleRadius startPoint:self.outerCircleView.center angle:angle];
}
//adjust the constraints to move the inner circle
self.innerCircleCenterXConstraint.constant += actualPosition.x - recognizer.view.center.x;
self.innerCircleCenterYConstraint.constant += actualPosition.y - recognizer.view.center.y;
[recognizer setTranslation:CGPointMake(0.0, 0.0) inView:self.view];
}
}
- (CGFloat)distanceBetweenStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
CGFloat xDif = endPoint.x - startPoint.x;
CGFloat yDif = endPoint.y - startPoint.y;
//pythagorean theorem
return sqrt((xDif * xDif) + (yDif * yDif));
}
- (CGPoint)findPointFromRadius:(CGFloat)radius startPoint:(CGPoint)startPoint angle:(CGFloat)angle {
CGFloat x = radius * cos(angle) + startPoint.x;
CGFloat y = radius * sin(angle) + startPoint.y;
return CGPointMake(x, y);
}
- (CGFloat)angleBetweenStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
CGPoint originPoint = CGPointMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
return atan2f(originPoint.y, originPoint.x);
}
This works almost perfectly. The problem is I try to find the percentage that the user moved towards the outside of the circle. So I use the distanceBetweenStartPoint(center of outer circle) endPoint(center of inner circle) method and divide that by the radius of the outer circle. This should give me a value of 1 when the circle has been dragged as far to one side as it can go. Unfortunately I am getting values like 0.9994324 or 1.000923. What could be causing this? Thanks for any insight!

iOS: derive angle of tap point given a circle

I have got a UIImageView displaying a circle divided in six equal triangles corresponding to:
area1 between 0-60 degrees
area2 between>60-120 degrees
area3 between>120-180 degrees
area4 between>180-240 degrees
area5 between>240-300 degrees
area6 between>300-360 degrees
The circle is similar to the following (pardon me for the bad drawing):
I would like to derive from the touch point in which area the tap is. For example if the user taps at the top right corner of the circle then the area should be area 2: ">60-120".
The input data I got is:
width and height of the frame containing the circle (e.g. 200 pixel wide, 200 pixels height)
tap point coordinates
Any suggestion on how to infer in which area the tap point falls given the input data described above?
I just noticed some errors in this solution I'd mentioned in the comments, but it's generally what you need...
I recommend getting the angle between your tap point and your circle's frame's center then implementing a getArea function to get the particular area tapped within your circle, ex:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint tapPoint = [touch locationInView:self.view];
CGFloat angle = [self angleToPoint:tapPoint];
int area = [self sectionForAngle:angle];
}
- (float)angleToPoint:(CGPoint)tapPoint
{
int x = self.circle.center.x;
int y = self.circle.center.y;
float dx = tapPoint.x - x;
float dy = tapPoint.y - y;
CGFloat radians = atan2(dy,dx); // in radians
CGFloat degrees = radians * 180 / M_PI; // in degrees
if (degrees < 0) return fabsf(degrees);
else return 360 - degrees;
}
- (int)sectionForAngle:(float)angle
{
if (angle >= 0 && angle < 60) {
return 1;
} else if (angle >= 60 && angle < 120) {
return 2;
} else if (angle >= 120 && angle < 180) {
return 3;
} else if (angle >= 180 && angle < 240) {
return 4;
} else if (angle >= 240 && angle < 300) {
return 5;
} else {
return 6;
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let tapPoint = touch?.location(in: self)
let distance = distanceWithCenter(center: circleCenter,SCCenter: tapPoint!)
if distance <= radius {
let angle = angleToPoint(tapPoint: tapPoint!)
print(angle)
}
func angleToPoint(tapPoint:CGPoint) -> CGFloat{
let dx = circleCenter.x - tapPoint.x
let dy = circleCenter.y - tapPoint.y
let radians = atan2(dy, dx) + π // Angel in radian
let degree = radians * (180 / π) // Angel in degree
print("angle is off = \(degree)")
return degree
}
You got the angle now check in which section it belong.You can figure it out by comparision.If you face any problem please let me know .

Rotating UIView (Again)

I'm encountering some problems with rotating a uiview again. This time, im trying to rotate a uiview 1/12 the speed of another uiview I'm rotating at normal speed. However, when I try to accomplish this task, the uiview I'm trying to move slower moves like this:
1st Update
https://www.youtube.com/watch?v=wj3nRJo5CMM&feature=youtu.be
2nd Update
https://www.youtube.com/watch?v=YLRkUzXSDtQ&feature=youtu.be
Here's my code:
- (void)rotateHand:(UIPanGestureRecognizer *)panGesture {
if ([(UIPanGestureRecognizer*)panGesture state] == UIGestureRecognizerStateBegan) {
CGPoint touchPoint = [panGesture locationInView:[self view]];
float dx = touchPoint.x - minHandContainer.center.x;
float dy = touchPoint.y - minHandContainer.center.y;
arcTanMin = atan2(dy,dx);
arcTanHour = atan2(hourHand.center.x - minHandContainer.center.x, hourHand.center.y - minHandContainer.center.y);
if (arcTanMin < 0) {
arcTanMin = 2 * M_PI + arcTanMin;
}
if (arcTanHour < 0) {
arcTanHour = 2 * M_PI + arcTanMin;
}
NSLog(#"arcTanMin %f", arcTanMin);
startTransformMin = minHandContainer.transform;
startTransformHour = hourHandContainer.transform;
}
if ([(UIPanGestureRecognizer*)panGesture state] == UIGestureRecognizerStateChanged) {
CGPoint pt = [panGesture locationInView:[self view]];
float dx = pt.x - minHandContainer.center.x;
float dy = pt.y - minHandContainer.center.y;
float ang = atan2(dy,dx);
if (ang < 0) {
ang = 2 * M_PI + ang;
}
float angleDifferenceM = arcTanMin - ang;
float angleDifferenceH = arcTanHour + angleDifferenceM * (1.0/12.0);
NSLog(#"angleDiffM %f", angleDifferenceM);
NSLog(#"angleDiffH %f", angleDifferenceH);
minHandContainer.transform = CGAffineTransformRotate(startTransformMin, -angleDifferenceM);
hourHandContainer.transform = CGAffineTransformRotate(startTransformHour, -angleDifferenceH);
}
}
It appears that you're using arcTanMin as the starting reference angle for both the minute hand and hour hand. So when you make the jump across the x-axis, both angleDifferenceM and angleDifferenceH are making the jump (which is why at the moment of the jump the angle of the hour hand to the y-axis is the same as the angle of the minute hand to the x-axis), but angleDifferenceH doesn't need to make the jump. Change this:
float angleDifferenceH = angleDifferenceM * (1.0/12.0);
to
float angleDifferenceH = arcTanHour + angleDifferenceM * (1.0/12.0);
with an appropriate starting value for arcTanHour.

onTouch Events not being called, when sprite moves off initial part of map?

It appears no onTouch events are being called (put NSLog statements in to confirm) when my player is moved above or to the right of my map. Map is big enough, and you can still see the map slightly when I get to the top or right, as the layer shifts as intended, but when clicking any further then what's initially loaded on screen, no touch events are being fired and char is stuck. Any ideas?
- (void)setCenterOfScreen:(CGPoint) position {
CGSize screenSize = [[CCDirector sharedDirector] viewSize];
int x = MAX(position.x, screenSize.width/2);
int y = MAX(position.y, screenSize.height/2);
x = MIN(x, theMap.mapSize.width * theMap.tileSize.width - screenSize.width/2);
y = MIN(y, theMap.mapSize.height * theMap.tileSize.height - screenSize.height/2);
CGPoint goodPoint = ccp(x,y);
CGPoint centerOfScreen = ccp(screenSize.width/2, screenSize.height/2);
CGPoint difference = ccpSub(centerOfScreen, goodPoint);
self.position = difference;
}
-(void) touchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLoc = [touch locationInNode:self];
// NOT CALLED WHEN PLAYER REACHES TOP OR RIGHT OF MAP (INITIAL SCREEN LOAD PART OF MAP)
// When clicking any furhter or top on the map, no touch event is fired and character (sprite) doesn't move
NSLog(#"Touch fired");
CGPoint playerPos = mainChar.position;
CGPoint diff = ccpSub(touchLoc, playerPos);
// Move horizontal or vertical?
if (abs(diff.x) > abs(diff.y))
{
if (diff.x > 0)
{
playerPos.x += theMap.tileSize.width;
}
else
{
playerPos.x -= theMap.tileSize.width;
}
}
else
{
if (diff.y > 0)
{
playerPos.y += theMap.tileSize.height;
}
else
{
playerPos.y -= theMap.tileSize.height;
}
}
if (playerPos.x <= (theMap.mapSize.width * theMap.tileSize.width) &&
playerPos.y <= (theMap.mapSize.height * theMap.tileSize.height) &&
playerPos.y >= 0 &&
playerPos.x >= 0)
{
mainChar.position = playerPos;
}
[self setCenterOfScreen:mainChar.position];
}
It is most likely your scene's content size. I believe you are moving the scene (CCScene) with the call to setCenterOfScreen as you move the player right? If I remember correctly the content size and bounding box for the scene doesn't change as you add children to it. If that is the case (or still the case) then you'll need to set the content size of the scene to the size of your entire level (map). You'll also want to ensure that the lower-left or your map aligns with the lower-left of the scene at (0,0) so that your scene's new bounding box encloses all of your map.
To check, after you've setup the scene CCLOG the scene's content size. If it is not the size of your map then you know that is the problem if you are indeed moving the scene with the call to setCenterOfScreen.
Another solution is to create a root content node that you add to the scene, then you add your game elements to that content node. That way the touch occurs on the scene but when you do the movements you move the content node, not the scene. For example:
#implementation YourScene
- (void)onEnter
{
[super onEnter];
self.contentNode = [CCNode node];
[self addChild:self.contentNode];
CCSprite* bg = ...;
CCSprite* player = ...;
[self.contentNode addChild:bg];
[self.contentNode addChild:player];
...
}
- (void)setCenterOfScreen:(CGPoint)position
{
CGSize screenSize = [[CCDirector sharedDirector] viewSize];
int x = MAX(position.x, screenSize.width/2);
int y = MAX(position.y, screenSize.height/2);
x = MIN(x, theMap.mapSize.width * theMap.tileSize.width - screenSize.width/2);
y = MIN(y, theMap.mapSize.height * theMap.tileSize.height - screenSize.height/2);
CGPoint goodPoint = ccp(x,y);
CGPoint centerOfScreen = ccp(screenSize.width/2, screenSize.height/2);
CGPoint difference = ccpSub(centerOfScreen, goodPoint);
self.contentNode.position = ccpAdd(self.contentNode.position, difference);
}
Hope this helps.

iOS using accelerometer to move an object within a circle

I am trying use the accelerometer to move an image within a circle. I am having issue that when the image hits the edge of the circle, it just moves the other side of the circle. My code is below:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
//NSLog(#"x : %g", acceleration.x);
//NSLog(#"y : %g", acceleration.y);
//NSLog(#"z : %g", acceleration.z);
delta.x = acceleration.x * 10;
delta.y = acceleration.y * 10;
joypadCap.center = CGPointMake(joypadCap.center.x + delta.x, joypadCap.center.y - delta.y);
distance = sqrtf(((joypadCap.center.x - 160) * (joypadCap.center.x - 160)) +
((joypadCap.center.y -206) * (joypadCap.center.y - 206)));
//NSLog(#"Distance : %f", distance);
touchAngle = atan2(joypadCap.center.y, joypadCap.center.x);
NSLog(#"Angle : %f", touchAngle);
if (distance > 50) {
joypadCap.center = CGPointMake(160 - cosf(touchAngle) * 50, 206 - sinf(touchAngle) * 50);
}
I was having the same issue when attempting to implement a circular spirit level using CMDeviceMotion. I found it was an issue with the coordinates passed to atan2(y,x). This function requires cartesian coordinates, with (0,0) in the centre of the view. However, the screen coordinates have (0,0) in the top left corner. I created methods to convert a point between the two coordinate systems, and now it's working well.
I put up a sample project here on github, but here's 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);
}

Resources