I have a situation where I have to work around a bug in iOS7 and need to convert the UIButton control events:
UIControlEventTouchUpOutside
UIControlEventTouchDown
UIControlEventTouchUpInside
UIControlEventTouchDragOutside
UIControlEventTouchDragInside
UIControlEventTouchDragInside
to UIGestureRecgonizer (I can't use UIView touch methods such as touchesBegan, touchesEnded, etc. either). I'm kind of interested if this is even possible anyway.
For instance, I'm thinking of a way to convert UIControlEventTouchDown and can't think of a way. UITapGestureRecognizer and UIPanGestureRecognizer both would not work.
Anyone know if this is possible?
I've done something similar with a UILongPressGestureRecognizer for a SpriteKit game to implement line segment intersection, pretty sure it would be easily adaptable for mimicking a UIButton too. Keep track of the previous location of your gesture recognizer, I just used a static CGPoint that I updated every time my selector fired. Then, just check the previous position and the current position with CGRectContainsPoint using your button's frame, and depending on those results, do whatever you need to do. If both are inside the frame, that's the same as UIControlEventDragInside, if both are outside, that's UIControlEventDragOutside, if the previous is outside and the current is inside, that's UIControlEventDragEnter, etc etc. Also make sure to check the gesture recognizer's state so you know when to call TouchUpInside/Outside. I'd make sure to have a damn good reason to do this, but it seems workable to me.
- (void) longPress:(UILongPressGestureRecognizer *)longPressGestureRecognizer
{
if (longPressGestureRecognizer.state == UIGestureRecognizerStateBegan || longPressGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
[self addHighlights];
}
else
{
[self removeHighlights];
}
}
else if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
if (self.highlightView.superview)
{
[self removeHighlights];
}
CGPoint touchedPoint = [longPressGestureRecognizer locationInView: self];
if (CGRectContainsPoint(self.bounds, touchedPoint))
{
if ([self.delegate respondsToSelector:#selector(buttonViewDidTouchUpInside:)])
{
[self.delegate buttonViewDidTouchUpInside:self];
}
}
}
}
Related
The player drags a sprite in my game but when accidentally touch the screen with a second finger it screws the movement obviously.
I used the following solutions for disable the second touch, but unfortunately it doesn't work:
//--------------
-(void)touchesBegan:(NSSet*) touches withEvent:(UIEvent*) event {
if (touches.count == 1 && draggedNode == nil) {
CGPoint pos = [[touches anyObject] locationInNode:self];
SKNode * touchedNode = [self nodeAtPoint:pos];
if([touchedNode.name isEqual: #"shooterBall"]){
draggedNode = touchedNode;
}
draggedNodeOffset = CGPointMake(draggedNode.position.x - pos.x, draggedNode.position.y - pos.y);
}
}
//--------------
-(void)touchesMoved:(NSSet*) touches withEvent:(UIEvent*) event {
if (touches.count <= 1) {
CGPoint pos = [[touches anyObject] locationInNode:self];
draggedNode.position = CGPointMake(pos.x + draggedNodeOffset.x, pos.y+draggedNodeOffset.y);
}
}
//--------------
-(void)touchesEnded:(NSSet*) touches withEvent:(UIEvent*) event {
draggedNode = nil;
}
//--------------
Do you have any solution for this?
Thanks your help in advance!
You want to implement UIPanGestureRecognizer in your scene. It will allow you to track the location of the user's touch and at the same time control other "stray" touches: UIPanGestureRecognizer Documentation
After you initialize it, you need to implement a method to handle the user's pans. You will have to set flags inside of this method to control when the swipe started/ended. I think this answer on StackOverflow gave a really good explanation of using it (with Swift). BTW, when you initialize it, you should set the gesture recognizer's property maximumNumberOfTouches to 1 (that will cause it to ignore other touches while the user is panning).
The trickier part will be to translate the same code you wrote before to gesture recognizer. The difference is that your handler will be called only once for each "swipe" or "pan", while the touches method you are using now is called each time there is a "touch". There are a few ways to proceed at this point, and you could try whatever you like, but I think that this would be the easiest way to go once you have your gesture recognizer set up (spoiler):
make sure the gesture recognizer is an instance variable so you can access it from all methods.
go to the update: method and make an if statement that checks if gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged
use the same algorithm that you had before in this if statement. To check the location that the touch is at use the method: locationInView:. Use self.view as the parameter.
Hope this helped! good luck.
I need to implement a thumb print scanner in swift with xcode. I found the gesture recogniser but I am missing the point where I can react to the "onTouchDown" and "onTouchUp" events.
the underlying object is an UIImageView. I am quite new to this and I can't find proper documentation on this.
Thanks a lot
With the current iOS devices, the following hardware is supported:
A screen that is multi-touch aware.
A home button that is able to read thumb-prints.
Therefore its not possible to implement a thumb-print scanner on a UIImageView or any UIView. You can however respond to touches, multi-touches, track acceleration, speed, movement, etc.
Responding to touch events:
There's a few ways to respond to touch events.
UIButton:
You could add a UIButton as a subview or your view. Be sure to size it to take up the parent view's bounds. Then use [button addTarget:self action:#selector(someMethod) controlEvents:UIControlEventTouchUpInside]
Use a UIPanGestureRecognizer:
Add a UIPanGestureRecognizer:
_panHandler = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(panHandle:)];
- (void)panHandle:(UIPanGestureRecognizer *)recognizer;
{
if ([recognizer state] == UIGestureRecognizerStateBegan) {
//do something
}
else if ([recognizer state] == UIGestureRecognizerStateChanged) {
//do something
}
else if ([recognizer state] == UIGestureRecognizerStateEnded) {
//do something
}
}
Lowest level / most flexible / more work:
In your UIView sub-class override:
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
}
So my current project has a pan gesture recognizer, and if I have panned to the top of the screen the screen it is supposed to scroll up to account for that gesture. While the gesture hasn't ended and the current position of the gesture remains at the top of the screen, I want to continually keep scrolling. My problem is the gesture recognizer only gets called when the state changes, therefore my content will only scroll if you move back and forth at the top, not continually while the gesture continues to be at the top. Is there any reasonable way to continually call code while the gesture hasn't ended, but isn't necessarily changing? Here is pseudo-code of what I have:
- (void)handleGestureRecognizer:(UIGestureRecognizer *)gesture {
if ( gesture.state == UIGestureRecognizerStateChanged ) {
CGPoint point = [gesture locationInView:self.view];
if (point.y < 100) {
//I would like this code to be called continually while the
//gesture hasn't ended, not necessarily only when it changes
[self updateScrollPosition];
}
}
I can think of a few ghetto ways to do it by setting state bools based on the current state of the recognizer and creating my own timer to periodically check, but it seems pretty hacky and I don't particularly like it, so I'm wondering if anyone could come up with a cleaner solution.
One way to use a timer and feel better about it would be to conceal it a little by using performSelector:withObject:afterDelay:
- (void)stillGesturing {
[self updateScrollPosition];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(stillGesturing) withObject:nil afterDelay:0.5];
}
// then in the recognizer target
if (gesture.state == UIGestureRecognizerStateEnded) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
} else if ( gesture.state == UIGestureRecognizerStateChanged ) {
CGPoint point = [gesture locationInView:self.view];
if (point.y < 100) {
//I would like this code to be called continually while the
//gesture hasn't ended, not necessarily only when it changes
[self stillGesturing];
}
}
I have a UIPanGestureRecognizer that I added to a UIScrollView, and when certain circumstances with the UIPanGestureRecognizer are met I want to cancel the touch/pan events from going to the UIScrollView, right now I have something like this:
-(void)panRecognized:(UIPanGestureRecognizer *)sender {
if(/* some logic */) {
[sender cancelsTouchesInView];
}
}
however calling [sender cancelsTouchesInView] does not stop scrolling from occurring in my UIScrollView.
I come from a big JavaScript background and I tend to expect events to behave in the same way and I think it's my downfall that they don't. I'm finding it really difficult to understand how events are handled in iOS.
-(void)panRecognized:(UIPanGestureRecognizer *)sender {
if(/* some logic */ && sender.state == UIGestureRecognizerStateBegan) {
[self.scrollView setUserInteractionEnabled:NO];
}
else if(sender.state == UIGestureRecognizerStateEnded)
{
[self.scrollView setUserInteractionEnabled:YES];
}
}
I am using UIPanGestureRecognizer to drag a UIButton around the screen. The idea is that the user can drag it over a folder to insert it in the folder (like iOS icons). This code I found works fine if I want to detect when the button overlaps with the image:
-(void) touchesEnded:(NSSet *) touches {
if(CGRectIntersectsRect([imageViewA frame], [imageViewB frame]) {
NSLog(#"Do something.");
}
}
But since the button is big and there are more images one next to another, it may happen that the button overlaps with both of them. I therefore want to detect when the actual user finger holding the UIButton overlaps with the image to trigger the right action. Any ideas?
UIGestureRecognizer will recognize the pan and when it ends, you can use locationInView: to find the finger's position in the button's super view. You can then see if they are overlapping with CGRectContainsPoint(frame, point):
- (void)handlePanGesture:(UIPanGestureRecognizer*)recognizer {
if ([recognizer state] == UIGestureRecognizerStateEnded) {
CGPoint fingerPoint = [recognizer locationInView:someImageView.superview];
if (CGRectContainsPoint(someImageView.frame, fingerPoint)) {
NSLog(#"Do something");
}
}
}