I'm using XCode 4.4 developing for iOS 5 on an iPad and am using the Storyboard layout when creating my custom button.
I have the touch event correctly working and logging but now I want to get the x/y coordinates of the tap on my custom button.
If possible, I'd like the coordinates to be relative to the custom button instead of relative to the entire iPad screen.
Here's my code in the .h file:
- (IBAction)getButtonClick:(id)sender;
and my code in the .m file:
- (IBAction)getButtonClick:(id)sender {
NSLog(#"Image Clicked.");
}
Like I said, that correctly logs when I tap the image.
How can I get the coordinates of the tap?
I've tried a few different examples from the internet but they always freeze when it displays a bunch of numbers (maybe the coordinates) in the log box. I'm VERY new to iOS developing so please make it as simple as possible. Thanks!
To get touch location you can use another variant of button action method: myAction:forEvent: (if you create it from IB interface note "sender and event" option in arguments field: )
Then in your action handler you can get touch location from event parameter, for example:
- (IBAction)myAction:(UIButton *)sender forEvent:(UIEvent *)event {
NSSet *touches = [event touchesForView:sender];
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:sender];
NSLog(#"%#", NSStringFromCGPoint(touchPoint));
}
Incase of Swift 3.0 the accepted answer works same except syntax will be changed as follows:
Swift 3.0:
#IBAction func buyTap(_ sender: Any, forEvent event: UIEvent) {
let myButton = sender as! UIButton
let touches = event.touches(for: myButton)
let touch = touches?.first
let touchPoint = touch?.location(in: myButton)
print("touchPoint\(touchPoint)")
}
For your overall coordinates (with reference to the screen), you need to create a CGPoint that contains the coordinates of your touch. But to do that, you need to get that touch first. So start by getting the touch event, then by making that point using the locationInViewmethod. Now, depending on when you want to log the touch - when the user touches down, or when they lift their finger -, you have to implement this code in the touchesBegan or touchesEnded method. Let's say you do touchesEnded, which passes an NSSet cales "touches" containing all the touch events.
UITouch *tap = [touches anyObject];
CGPoint touchPoint = [tap locationInView:self.view];
"touchPoint" will now contain the point at which the user lifts their finger. To print out the coordinates, you just access the x and y properties of that point:
CGFloat pointX = touchPoint.x;
CGFloat pointY = touchPoint.y;
NSLog(#" Coordinates are: %f, %f ", pointX, pointY);
That should output the coordinates of the touch. Now to have it be referenced to whatever button you're using, I would suggest you just manually subtract the values for the button's coordinates from the point. It seems like a simple solution, and honestly I don't know a way of getting coordinates with reference to another object, unless you make a view based on that object, and pass it to locationInView instead of self.view.
For more info on touches, there's a great set of tutorials here.
Related
I am working on Atlas App in which I am displaying map which I can zoom and pan using pdf file. I am using vfr reader for this purpose and it is working fine. I want to detect the touch location so that I can get the correct state selected. I am getting the correct coordinate when view is not zoomed and panned using the code below:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:theScrollView];
}
But,when I zoom it out and pan it,the touch location changes and I am not getting the correct state selected. How will I get the correct selected state?
On debugging vfr reader classes I found that I can get the correct exact location of touch in ReaderContentPage class. This class gives the correct touch location after zooming also. You can get the point in processingSingleTap method as below:
- (id)processSingleTap:(UITapGestureRecognizer *)recognizer
{
CGPoint point = [recognizer locationInView:self];
}
CGPoint point gives the correct touch location. And then use the delegate method to get the correct coordinates in the required class.
So I'm making a minesweeper clone for iOS, and I have an array of UIButtons containing 135 buttons (the minesweeper board). It looks great and theoretically should work great. But I was having trouble detecting which button was being hit. I tried working around the problem by using this code;
UITouch *touched = [[event allTouches] anyObject];
CGPoint location = [touched locationInView:touched.view];
NSLog(#"x=%.2f y=%.2f", location.x, location.y);
int pointX = location.x;
int pointY = location.y;
My goal was to grab the coordinates of the touch and then use some basic math to figure out which button was being pressed. However, it doesn't work. At all. No button is pressed, no function runs, essentially nothing happens. I'm left with a minesweeper board that you can't interact with. Any ideas?
Assign a separate number to the tag of each button. Use the button's target, not the UITouch code. When you get a buttonPress, query the tag.
You could subclass the buttons and then program the what needs to happen when a touch occurs in a button inside of that subclass.
The UIButton * can be accessed by calling:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
on self (a UIView * I imagine). So I suppose you can set the button to the pushed state, and when touchesEnded: is called, set it back.
I have a custom view that has multiple subviews. They are all circles on the screen, sort of like three wheels of different radius on top of each other. I'm trying to make them receive a UITouch * event correctly to make them spin with the finger. Since the shapes are actually squares on the screen, when a bigger one flips and it's touchable area enters the frame of a circle above, it becomes untouchable.
So, I created another subview on top of others that will calculate the distance of the touch point to the center and distribute the touch event accordingly. I can think of several ways of doing it, but I was wondering what would be the most elegant, and most correct way of handling a situation like this.
This is what I've done so far: My custom view has a delegate, and that delegate is assigned to my main viewController. I have three protocol methods in my custom view, for the three wheels respectively. I'm passing out the touch and event according to the point of UITouch, but I'm not sure how should I actually send this data to the views that are supposed to receive it. They are all custom UIControl objects, and they all handle touches via the -beginTrackingWithTouch:withEvent:. Since this is a private method, I cannot access this from my viewController. Should I make this method public and access this from the viewController, or is there a more correct way of handling this?
Edit: added the code:
This is how I distribute the touch in the custom UIView object. The calculations work fine.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Distribute the touches according to the touch location.
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
//calculations for the circles.
CGFloat xDistance = (point.x - BIGGEST_CIRCLE_RADIUS);
CGFloat yDistance = (point.y - BIGGEST_CIRCLE_RADIUS);
CGFloat distance = sqrtf((xDistance*xDistance) + (yDistance*yDistance));
//Check to see if the point is in one of the circles, starting from the innermost circle.
if (distance <= SMALLEST_CIRCLE_RADIUS) {
[self.delegate smallestCircleReceivedTouch:touch withEvent:event];
} else if (distance < MIDDLE_CIRCLE_RADIUS) {
[self.delegate middleCircleReceivedTouch:touch withEvent:event];
} else if (distance <= BIGGEST_CIRCLE_RADIUS) {
[self.delegate biggestCircleReceivedTouch:touch withEvent:event];
} else {
return;
}
}
The delegate is the viewController and the circles are custom UIControls. They handle the touch like this:
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchPoint = [touch locationInView:self];
{....}
return YES;
}
These work fine in themselves, but I'm not sure how should I connect the delegate method to the touch handling of each custom UIControl. Should I call their -beginTrackingWithTouch:withEvent: from the viewController, or should I make them implement the protocol of the customView? Or is there some other way to handle this properly?
Even I did not try it, it is not necessary, to do your own calculation. -hitTest:withEvent: should work fine for you.
I'm trying to create a program in which when I tap in roughly the same location as where my last touch ended, a circle changes color. I've tried using the previousLocationInView method using logic along the lines of: "if touchesbegan location == previousTouchesLocation, change color to blue." This method didn't work because the touchesBegan and previousTouchLocation coordinates have the same value which always changed the color whenever I tapped the screen regardless of the location. I get the same result if I substitute previousTouchLocation with touchesEnded. Here is the code if anyone thinks that it could help.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
touchLocation = [touch locationInView:self.view];
lastTouchLocation = [[touches anyObject] previousLocationInView:self.view];
distance_x = touchLocation.x - cvx;
distance_y = cvy - touchLocation.y;
previousDistance_x = lastTouchLocation.x - cvx;
previousDistance_y = cvy - lastTouchLocation.y;
if ((previousDistance_x == distance_x) && (previousDistance_y == distance_y)) {
if (([cv.color isEqual:[UIColor redColor]])) {
[cv setColor:[UIColor blueColor]];
}
else
[cv setColor:[UIColor redColor]];
}
}
-previousLocationInView: gives you the previous location of a touch that's currently moving on the screen. It doesn't give you the location of a different, now ended, touch.
What you're going to have to do is, on the first touch, store the touch's location in a property or ivar. Then, on the second touch, compare its location to the stored location. You'll need to allow for some leeway—fingers are not pixel-accurate in the way mice can be—so a touch that's moved ten or twenty pixels should probably count as a double-tap.
Actually, come to think of it, your best bet might be to use a UITapGestureRecognizer with numberOfTapsRequired set to 2. This will handle all the detection logic for you, and you can combine it with other gesture recognizers to build up more complex behaviors.
I have a virtual keyboard in my app with 6 keys, and the whole thing is just an image implemented with UIImageView. I determined the exact x and y coordinates that correspond to the image of each 'key' and used the following code to respond to a user interacting with the keyboard:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [[event allTouches] anyObject];
CGPoint point = [touch locationInView:touch.view];
if(point.y < 333 && point.y > 166 && point.x < 72 && point.x > 65)
{
NSLog(#"Key pressed");
}
//Repeat per key...
}
However, I have realized that this method is not very smart, because changing phone perspectives (portrait to landscape) or changing devices will ruin my x and y coordinates and therefore cause problems.
So, I am looking for an alternative to specifying the absolute x and y values, and using touchesMoved in general. Ideally, it would be a button with specific settings that would call its method if it was tapped, or if the user dragged their finger into the area of the button (even if very slowly - I used swipe detection before and it required too much of an exaggerated movement).
Is it possible to set up a button to call its method if tapped or if a touch event started outside of the button and then proceded into the button? If not, what are my alternatives?
Thanks SE!
You need to get the winSize property, which will fix the problem you are having with screen sizes.
CGSize size = [[CCDirector sharedDirector]winSize];
I do believe you are using Cocos2D? If so you can use this size property instead of hard coding numbers. :)
to convert your point, use
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
To see if its within the bounds/box of the button you could try:
if (CGRectContainsPoint ([self.myButton boundingBox], location)
{
//Execute method
}
This is all assuming you are using Cocos2D and a CCSprite for your button.
This should work on any screen size and portrait or landscape :)