0

If you have a normal UITextView with loads of text in it you can swipe up and down:

  • if you swipe left or right in a very horizontal line, nothing will happen
  • if you swipe left or right with a very slight angle, you will swipe up or down

So there is obviously some kind of class definition which tells the UITextView when to react to a swipe and when not to. Something like a maximumVariance constant, I guess.

My problem is that I'm trying to subclass an UITextView to enable left/right swipe. However, you will need to swipe in a pretty straight line, otherwise, you'll get an up or down swipe. So my question is if there is someway to re-define the standard variance variable of an UITextView?

Here is my subclass .m file. Note that the kMaximumVariance doesn't really have any effect as this is overwritten by whatever variance the standard UITextView class provides:

//
//  SwipeTextView.m
//  Swipes
//

#import "SwipeTextView.h"
#import "PageView.h"

#define kMinimumGestureLength    15
#define kMaximumVariance         100

@implementation SwipeTextView
@synthesize delegate, gestureStartPoint;


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];

    UITouch *touch =[touches anyObject];
    gestureStartPoint = [touch locationInView:self];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    [super touchesMoved:touches withEvent:event];


}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    [self resignFirstResponder]; // this hides keyboard if horizonal swipe

    UITouch *touch = [touches anyObject];
    CGPoint currentPosition = [touch locationInView:self];

    CGFloat deltaXX = (gestureStartPoint.x - currentPosition.x); // positive = left, negative = right
    CGFloat deltaYY = (gestureStartPoint.y - currentPosition.y); // positive = up, negative = down

    CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x); // will always be positive
    CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y); // will always be positive


    NSLog(@"deltaXX (%.1f) deltaYY (%.1f) deltaX (%.1f) deltaY (%.1f)",deltaXX, deltaYY, deltaX, deltaY);

    if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
        if (deltaXX > 0) {
            NSLog(@"Horizontal Left swipe - FINISHED detected");
        }
        else {
            NSLog(@"Horizontal Right swipe - FINISHED detected");
        }

    }



} 




@end

EDIT:

Suggestion to cancel touches reaching UITextView (see below):

// Prevent recognizing touches
- (BOOL)gestureRecognizer:(UISwipeGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionLeft) {
        // Left swipe: I want to handle this
        return YES;
    }
    if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionRight) {
        // Right swipe: I want to handle this
        return YES;
    }
    if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionUp) {
        // Up swipe: textView needs to handle this
        return NO;
    }
    if ([gestureRecognizer direction] == UISwipeGestureRecognizerDirectionDown) {
        // Down swipe: textView needs to handle this
        return NO;
    } else
        return YES;
}


-(void)viewDidLoad
{

    UITapGestureRecognizer *GesRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doSomething:)];

    [GesRec setCancelsTouchesInView:NO];
    [self addGestureRecognizer:GesRec];
    [GesRec release];

}


-(void)doSomething
{
    NSLog(@"doSomething");
}
DShah
  • 9,768
  • 11
  • 71
  • 127
n.evermind
  • 11,944
  • 19
  • 78
  • 122

2 Answers2

1

Try adding an UISwipeGestureRecognizer to the view and set cancelsTouchesInView to false so the swipe gesture is not passed through. This should let you detect left and right motion more accurately.

If this doesn't do the trick, implement your own subclass of UIGestureRecognizer and add that instead.

Edit

You add the gesture recognizer to the UITextView in your controller's viewDidLoad method.

In your delegate methods, you can't return NO in gestureRecognizer:shouldReceiveTouch: because it means the gesture recognizer won't examine the incoming touch object. In your case you want to return YES when the swipe is horizontal and NO otherwise, so vertical swipes are passed through while horizontal swipe is completed handled by you.

freespace
  • 16,529
  • 4
  • 36
  • 58
  • Thanks! However, I'm not quite sure how and where to implement this. I made a start (see edited post above), but I guess this isn't really what you mean, right? Where would I need to add an instance of UISwipeGestureRecognizer? In my viewDidLoad? Sorry if this is basic, but I am still a beginner. – n.evermind Sep 29 '11 at 13:46
  • Also, I'm not quite sure about the overall concept: The goal would be to control which touches reach the UITextView, right? I.e. to cancel those touches at angles which are not within the limits of my variance variable? – n.evermind Sep 29 '11 at 13:48
  • Sorry, I feel really stupid, but I still don't get it. I edited my post (see above)- but I still don't really understand why I need to add UIGestureRecognizer (it's not doing anything at the moment). Also, I'm not sure about how to prevent UITextView to handle the left/right swipes and change the accuracy of swipe directions. So in a way, I'm back to square one- I still have no idea of how to adjust the swipe direction according to my needs. Thanks for your help! – n.evermind Sep 29 '11 at 15:46
1

No subclass needed. set textView.userInteractionEnabled to NO and than put a transparent UIView over your textView. handle touches and gestures anyway you want on that UIView and make the textView respond to these programatically.

Zigglzworth
  • 6,645
  • 9
  • 68
  • 107
  • I did this with another project, but it proved to be a pain. I couldn't really figure out how to pass on a touch (which was made on the transparent UIView) to the UITextView or UITextField... Also, I sometimes needed to remove this transparent UIView. So in short, it was a pain and felt not very elegant. – n.evermind Sep 29 '11 at 13:53
  • Why would you need to pass a touch to the textView? all touch actions on the uiview can be turned into programmatic calls on the uitextview. Not so elegant I agree but it works. – Zigglzworth Sep 29 '11 at 18:40
  • So, for example, how would you pass on a touch on the uiview to the uitextview programatically? Imagine touching on the lower region of a transparanet uiview beneath which there is an uitextview of the same size (320x480). Now I want the tap to be passed on to the uitextview- how would you go about this? – n.evermind Sep 30 '11 at 07:24
  • Could you not get the point of the touch and possibly change the cursor position on the uitextview ? – Zigglzworth Sep 30 '11 at 08:02
  • No. That's exactly the problem. Whenever the user would touch the screen, I had to place the cursor on the very beginning of the textview. I couldn't figure out how to pass on the point of touch- it always seems to be 'eaten up' by the invisible view, however much I tried to pass it on to the UITextView. – n.evermind Sep 30 '11 at 12:06
  • Hmm.. ok so this might be something that needs to be achieved by playing around with the layers or subviews of uitextview. Eventually you will find the one controlling the scollview and this scrollview will need to scrollingEndabled turned off and than a gesturerecognizer added to it... Just a thought – Zigglzworth Sep 30 '11 at 18:00