UIScreenEdgePanGestureRecognizer failing to recognize gesture on right edge - ios

I am having an issue where i have defined a UIScreenEdgePanGestureRecognizer to detect pan gesture appearing on the right edge of my device but the gesture is recognized sporadically:
I have the following code:
_swipeInLeftGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipeInFromRightEdge:)];
_swipeInLeftGestureRecognizer.minimumNumberOfTouches = 1;
_swipeInLeftGestureRecognizer.maximumNumberOfTouches = 1;
[_swipeInLeftGestureRecognizer setEdges:UIRectEdgeRight];
[self.view addGestureRecognizer:_swipeInLeftGestureRecognizer];
- (void)handleSwipeInFromRightEdge:(UIGestureRecognizer*)sender
{
NSLog(#"swipe from right edge!!!!");
}
The gesture is attached to a view with nothing on it.
Am I missing something?

I've managed to create a workaround. It is pretty straightforward. I've subclassed UIWindow and used touchesBegan/touchesMoved/etc. methods to simulate gesture recognition.
It works. UIWindow is not rotated automatically, so I have to transform touch coordinates accordingly.
Here is my version of the transformation:
- (CGPoint)transformPoint:(CGPoint)point {
CGPoint pointInView = point;
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown) {
pointInView.x = self.bounds.size.width - pointInView.x;
pointInView.y = self.bounds.size.height - pointInView.y;
} else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) {
CGFloat x = pointInView.x;
CGFloat y = pointInView.y;
pointInView = CGPointMake(self.bounds.size.height - y, x);
} else if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight) {
CGFloat x = pointInView.x;
CGFloat y = pointInView.y;
pointInView = CGPointMake(y, self.bounds.size.width - x);
}
return pointInView;
}

I think there's bugs. My app is always in landscape mode, and I've set up the same thing as you, with it detecting the right edge. If that edge is the camera side of the iPad, it detects sporadically. If I flip over the iPad and the right edge is the button side, it works just fine. In fact, I'm having this problem with any gesture, not just this one.

Related

Text disappears after UITextView zoom and rotation

I added a UIPinchGestureRecognizer and UIRotationGestureRecognizer on UITextView. When I do a pinch or rotation, I change the transform property of my UITextView object. However, if I rotate it at some angle and zoom in to some scale, the text will suddenly disappear.
If I continue to rotate it, the text can reappear at some angle.
Is there any way to fix the disappearance?
Here's what I did with the gesture recognizer methods.
- (void)onPinchtextView:(UIPinchGestureRecognizer *)pinchGestureRecognizer {
CGFloat scale = pinchGestureRecognizer.scale;
if (pinchGestureRecognizer.state == UIGestureRecognizerStateChanged || pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
self.textView.transform = CGAffineTransformScale(self.textView.transform, scale, scale);
pinchGestureRecognizer.scale = 1.f;
}
- (void)onRotatetextView:(UIRotationGestureRecognizer *)rotateGestureRecognizer {
CGFloat rotation = rotateGestureRecognizer.rotation;
if (rotateGestureRecognizer.state == UIGestureRecognizerStateChanged || rotateGestureRecognizer.state == UIGestureRecognizerStateBegan) {
self.textView.transform = CGAffineTransformRotate(self.textView.transform, rotation);
[rotateGestureRecognizer setRotation:0];
}
}

Enable UIScrollView scrolling for particular CGPoint on UISwipeGestureRecognizer

I want to enable scrolling of UIScrollView only when i swipe from screen boundaries. I am using swipe gesture recognizer.
here is my code
-(void)swipeHandlerLeft:(UISwipeGestureRecognizer *)swipeRecognizer
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
point = [swipeRecognizer locationInView:self.view];
if(swipeRecognizer.direction == UISwipeGestureRecognizerDirectionLeft)
{
if (point.x > screenWidth - 10)
{
NSLog(#"Swipped Left");
_scrollView.scrollEnabled = YES;
}
else
{
_scrollView.scrollEnabled = NO;
}
}
}
-(void)swipeHandlerRight:(UISwipeGestureRecognizer *)swipeRecognizer
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
point = [swipeRecognizer locationInView:self.view];
if(swipeRecognizer.direction == UISwipeGestureRecognizerDirectionRight)
{
if (point.x < 10)
{
NSLog(#"Swipped Right");
_scrollView.scrollEnabled = YES;
}
else
{
_scrollView.scrollEnabled = NO;
}
}
}
Problem with above code is it works until scrollview is not enabled,but once it is enabled this methods are not getting called and i think the reason for that is as i've added scrollview inside of my UIView.
any help will be appreciated.
UIScrollView already has UIGestureRecognizer inside of it.
I think you would need to connect your code to those gestureRecognizers. You can access them by myScrollView.panGestureRecognizer and myScrollView.pinchGestureRecognizer.
Your scrollview catches your touch before your swipe view, and I suppose the swipe gesture recognizer is on the superview of the scroll view.
You must create a dependency by calling requireGestureRecognizerToFail(_:)
on your scrollView pan gesture recognizer, and passing your swipe gesture.

Sprite-Kit Pinch to Zoom Problems UIPinchGestureRecognizer

I've been working on this code for quite a while now but it just feels like one step forward and two steps back. I'm hoping someone can help me.
I'm working with Sprite Kit so I have a Scene file that manages the rendering, UI and touch controls. I have an SKNode thats functioning as the camera like so:
_world = [[SKNode alloc] init];
[_world setName:#"world"];
[self addChild:_world];
I am using UIGestureRecognizer, so I add the ones I need like so:
_panRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePanFrom:)];
[[self view] addGestureRecognizer:_panRecognizer];
_pinchRecognizer = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(handlePinch:)];
[[self view] addGestureRecognizer:_pinchRecognizer];
The panning is working okay, but not great. The pinching is the real problem. The idea for the pinching is to grab a point at the center of the screen, convert that point to the world node, and then move to it while zooming in. Here is the method for pinching:
-(void) handlePinch:(UIPinchGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
_tempScale = [sender scale];
}
if (sender.state == UIGestureRecognizerStateChanged) {
if([sender scale] > _tempScale) {
if (_world.xScale < 6) {
//_world.xScale += 0.05;
//_world.yScale += 0.05;
//[_world setScale:[sender scale]];
[_world setScale:_world.xScale += 0.05];
CGPoint screenCenter = CGPointMake(_initialScreenSize.width/2, _initialScreenSize.height/2);
CGPoint newWorldPoint = [self convertTouchPointToWorld:screenCenter];
//crazy method why does this work
CGPoint alteredWorldCenter = CGPointMake(((newWorldPoint.x*_world.xScale)*-1), (newWorldPoint.y*_world.yScale)*-1);
//why does the duration have to be exactly 0.3 to work
SKAction *moveToCenter = [SKAction moveTo:alteredWorldCenter duration:0.3];
[_world runAction:moveToCenter];
}
} else if ([sender scale] < _tempScale) {
if (_world.xScale > 0.5 && _world.xScale > 0.3){
//_world.xScale -= 0.05;
//_world.yScale -= 0.05;
//[_world setScale:[sender scale]];
[_world setScale:_world.xScale -= 0.05];
CGPoint screenCenter = CGPointMake(_initialScreenSize.width/2, _initialScreenSize.height/2);
CGPoint newWorldPoint = [self convertTouchPointToWorld:screenCenter];
//crazy method why does this work
CGPoint alteredWorldCenter = CGPointMake(((newWorldPoint.x*_world.xScale - _initialScreenSize.width)*-1), (newWorldPoint.y*_world.yScale - _initialScreenSize.height)*-1);
SKAction *moveToCenter = [SKAction moveTo:alteredWorldCenter duration:0.3];
[_world runAction:moveToCenter];
}
}
}
if (sender.state == UIGestureRecognizerStateEnded) {
[_world removeAllActions];
}
}
I've tried many iterations of this, but this exact code is what is getting me the closest to pinching on a point in the world. There are some problems though. As you get further out from the center, it doesn't work as well, as it pretty much still tries to zoom in on the very center of the world. After converting the center point to the world node, I still need to manipulate it again to get it centered properly (the formula I describe as crazy). And it has to be different for zooming in and zooming out to work. The duration of the move action has to be set to 0.3 or it pretty much won't work at all. Higher or lower and it doesn't zoom in on the center point. If I try to increment the zoom by more than a small amount, it moves crazy fast. If I don't end the actions when the pinch ends, the screen jerks. I don't understand why this works at all (it smoothly zooms in to the center point before the delay ends and the screen jerks) and I'm not sure what I'm doing wrong. Any help is much appreciated!
Take a look at my answer to a very similar question.
https://stackoverflow.com/a/21947549/3148272
The code I posted "anchors" the zoom at the location of the pinch gesture instead of the center of the screen, but that is easy to change as I tried it both ways.
As requested in the comments below, I am also adding my panning code to this answer.
Panning Code...
// instance variables of MyScene.
SKNode *_mySkNode;
UIPanGestureRecognizer *_panGestureRecognizer;
- (void)didMoveToView:(SKView *)view
{
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanFrom:)];
[[self view] addGestureRecognizer:_panGestureRecognizer];
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(-translation.x, translation.y);
_mySkNode.position = CGPointSubtract(_mySkNode.position, translation);
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
// No code needed for panning.
}
}
The following are the two helper functions that were used above. They are from the Ray Wenderlich book on Sprite Kit.
SKT_INLINE CGPoint CGPointAdd(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x + point2.x, point1.y + point2.y);
}
SKT_INLINE CGPoint CGPointSubtract(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x - point2.x, point1.y - point2.y);
}

Custom Gesture for Increase the size of the view

How can i write custom gesture for increase the size of the view in all the direction (i.e) in origin side and size of the view.
Example:
the user should touch the view in any one of the corner. either near to origin side or view end side.
Example: View frame size is (100,100,200,200). and minimum touch distance is 15 pixel from any side.
so, if the user touch location is (X=15,Y=40) in
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
method means that touch near to the origin side.
same if the user touch location is (X=15, Y=190) in
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
method means the touch is near to the size side.
My code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if([touches count] != self.touchMinimumCount ||
[[touches anyObject] tapCount] > self.tapMinimumCount)
{
self.state = UIGestureRecognizerStateFailed;
return;
}
self.startPoint = [[touches anyObject] locationInView:self.view];
self.viewFrame = self.view.frame;
if(((self.startPoint.x - self.view.bounds.origin.x) <= self.minimumTouchDistance) ||
((self.startPoint.y - self.view.bounds.origin.y) <= self.minimumTouchDistance) ||
((ABS(self.startPoint.x - self.view.bounds.size.width)) <= self.minimumTouchDistance) ||
((ABS(self.startPoint.y - self.view.bounds.size.height)) <= self.minimumTouchDistance))
{
self.state = UIGestureRecognizerStatePossible;
if(((self.startPoint.x - self.view.bounds.origin.x) <= self.minimumTouchDistance) ||
((self.startPoint.y - self.view.bounds.origin.y) <= self.minimumTouchDistance) )
{
self.currentTouchPosition = touchPositionIsOrigin;
}
else if(((ABS(self.startPoint.x - self.view.bounds.size.width)) <= self.minimumTouchDistance) ||
((ABS(self.startPoint.y - self.view.bounds.size.height)) <= self.minimumTouchDistance))
{
self.currentTouchPosition = touchPositionIsSize;
}
}
else
{
self.state = UIGestureRecognizerStateFailed;
}
}
My gesture possible state setting if the touch in side the touch distance only.
Now i want to resize the view frame according to the user finger move.
if the user touch the origin side and move towards the left direction means, want to change the origin.x of the view and the width also want to increase.
if the user touch the origin side and move towards the top direction means, want to change the origin.y of the view and the width also want to increase.
if the user touch the origin side and move towards the right direction means, want to change the origin.x of the view.
if the user touch the origin side and move towards the bottom direction means, want to change the origin.y of the view.
if the user touch the size side and move towards the left direction means, want to reduce the width of the view.
if the user touch the size side and move towards the top direction means, want to reduce the height of the view.
if the user touch the size side and move towards the right direction means, want to increase the width of the view.
if the user touch the size side and move towards the bottom direction means, want to increase the height of the view.
this all change according tho the user finger in that view while moving.
My code:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if(self.state == UIGestureRecognizerStateFailed) return;
direction currentDirection;
CGPoint currentLocation = [[touches anyObject] locationInView:self.view];
if(self.state == UIGestureRecognizerStatePossible ||
self.state == UIGestureRecognizerStateChanged)
{
if((currentLocation.x - self.startPoint.x) > 0)
{
currentDirection = directionRight;
self.state = UIGestureRecognizerStateChanged;
if(self.currentTouchPosition == touchPositionIsOrigin)
{
distanceInOriginX = // need to implement.
distanceInSizeX = // need to implement.
}
else if(self.currentTouchPosition == touchPositionIsSize)
{
distanceInSizeX = // need to implement.
}
}
else if((currentLocation.x - self.startPoint.x) < 0)
{
currentDirection = directionLeft;
self.state = UIGestureRecognizerStateChanged;
if(self.currentTouchPosition == touchPositionIsOrigin)
{
distanceInOriginX = // need to implement.
distanceInSizeX = // need to implement.
}
else if(self.currentTouchPosition == touchPositionIsSize)
{
distanceInSizeX = // need to implement.
}
}
else if((currentLocation.y - self.startPoint.y) >= 0)
{
currentDirection = directionBottom;
self.state = UIGestureRecognizerStateChanged;
if(self.currentTouchPosition == touchPositionIsOrigin)
{
distanceInOriginY = // need to implement.
distanceInSizeY = // need to implement.
}
else if(self.currentTouchPosition == touchPositionIsSize)
{
distanceInSizeY = // need to implement.
}
}
else if((currentLocation.y - self.startPoint.y) < 0)
{
currentDirection = directionTop;
self.state = UIGestureRecognizerStateChanged;
if(self.currentTouchPosition == touchPositionIsOrigin)
{
distanceInOriginY = // need to implement.
distanceInSizeY = // need to implement.
}
else if(self.currentTouchPosition == touchPositionIsSize)
{
distanceInSizeY = // need to implement.
}
}
else
{
currentDirection = directionUnknown;
}
self.originPoint = CGPointMake(distanceInOriginX, distanceInOriginY);
self.sizePoint = CGPointMake(distanceInSizeX, distanceInSizeY);
}
}
i mentioned the // need to implement. where ever i want the code for to achieve the functionality which i mentioned in that points. so, kindly help to go ahed.
if my was is wrong means, share the best way to do the same type of gesture.
any example is there also kindly share here. thanks in advance....
If you want to scale the size of a view, then use UIPinchGestureRecognizer. An example code is given here
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:#selector(scale:)];
pinchRecognizer.delegate = self;
[self addGestureRecognizer:pinchRecognizer];
handler method is
#pragma mark - Gesture handling functions
-(void)scale:(UIPinchGestureRecognizer*)recognizer {
if ([recognizer state] == UIGestureRecognizerStateBegan) {
_lastScale = 1.0;
}
CGFloat scale = 1.0 - (_lastScale - [recognizer scale]);
CGAffineTransform currentTransform = self.imgView.transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
self.imgView.transform = newTransform;
_lastScale = [recognizer scale];
}

UIPanGestureRecognizer stop calling the selector

I'm having trouble to work with UIPanGestureRecognizer as it just calls the selector when I have my finger moving, I want it to keep calling the selector even my finger is standing at the same place.
There are 4 objects on the screen one at the top, one at the right side, one at the left side and one at the bottom. I have an object at the center of the screen (this is the one I'm moving with the panGesture). When this object touches the others I want it to give me a Log, it works when it touches but if I keep my finger at the same place it stops to give me logs, if I move a little it starts to give me logs again.
Is there anyway I can keep calling the selector even with my finger at the same place?
here is a code example:
- (void)moveObject:(UIPanGestureRecognizer *)sender
{
CGPoint translation = [sender translationInView:self.limiteDirecional];
[sender setTranslation:CGPointMake(0, 0) inView:self.limiteDirecional];
CGPoint center = sender.view.center;
center.y += translation.y;
int yMin = 0;
int yMax = self.limiteDirecional.frame.size.height;
if (center.y < yMin || center.y > yMax )
return;
sender.view.center = center;
center.x += translation.x;
int xMin = self.limiteDirecional.frame.size.width;
int xMax = 0;
if (center.x > xMin || center.x < xMax)
return;
sender.view.center = center;
if (CGRectIntersectsRect(sender.view.frame,self.Top.frame)) {
NSLog(#"TOP");
}
if (CGRectIntersectsRect(sender.view.frame,self.Botton.frame)) {
NSLog(#"BOTTON");
}
if (CGRectIntersectsRect(sender.view.frame,self.Right.frame)) {
NSLog(#"RIGHT");
}
if (CGRectIntersectsRect(sender.view.frame,self.Left.frame)) {
NSLog(#" LEFT");
}
if (sender.state == UIGestureRecognizerStateEnded) {
sender.view.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
}
}
I'm not entirely following the logic of your routine, so I'll provide a generic template of what a solution might look like when you want continuous events in the middle of a gesture, whether the user is moving their finger or not. Hopefully you can adapt this technique for your own purposes.
This uses CADisplayLink, which is considered a better technique for animation than the older technique of using a NSTimer. To use CADisplayLink, though, you need to add the needed framework, QuartzCore.framework, to your project, if you haven't already. Also note that in my gesture recognizer, I'm checking the state of a gesture, to know whether we're starting a gesture, in the middle of one, or ending one:
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (nonatomic) CGPoint translationInView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(handleGesture:)];
// I'm adding to the main view, but add it to whatever you want
[self.view addGestureRecognizer:gesture];
}
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
NSLog(#"%s translationInView = %#", __FUNCTION__, NSStringFromCGPoint(self.translationInView));
// Do here whatever you need to happen continuously while the user is in the
// middle of the gesture, whether their finger is moving or not.
}
- (void)handleGesture:(UIPanGestureRecognizer *)gesture
{
self.translationInView = [gesture translationInView:gesture.view];
if (gesture.state == UIGestureRecognizerStateBegan)
{
[self startDisplayLink];
// Do whatever other initialization stuff as the user starts the gesture
// (e.g. you might alter the appearance of the joystick to provide some
// visual feedback that they're controlling the joystick).
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
// Do here only that stuff that actually changes as the user is moving their
// finger in the middle of the gesture, but which you don't need to have
// repeatedly done while the user's finger is not moving (e.g. maybe the
// visual movement of the "joystick" control on the screen).
}
else if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed)
{
[self stopDisplayLink];
// Do whatever other cleanup you want to do when the user stops the gesture
// (e.g. maybe animating the moving of the joystick back to the center).
}
}
#end
You can achieve a similar effect if you use NSTimer, too. Whatever works better for you.

Resources