iPhone - UIWebView Gestures - iphone

I have viewcontrollers set up to track gestures and commit an action, in this case, change the tab. The code for the viewcontrollers is as follows:
#define HORIZ_SWIPE_DRAG_MIN 100
CGPoint mystartTouchPosition;
BOOL isProcessingListMove;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint newTouchPosition = [touch locationInView:self.view];
if(mystartTouchPosition.x != newTouchPosition.x || mystartTouchPosition.y != newTouchPosition.y) {
isProcessingListMove = NO;
}
mystartTouchPosition = [touch locationInView:self.view];
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint currentTouchPosition = [touch locationInView:self.view];
// If the swipe tracks correctly.
double diffx = mystartTouchPosition.x - currentTouchPosition.x + 0.1; // adding 0.1 to avoid division by zero
double diffy = mystartTouchPosition.y - currentTouchPosition.y + 0.1; // adding 0.1 to avoid division by zero
if(abs(diffx / diffy) > 2.5 && abs(diffx) > HORIZ_SWIPE_DRAG_MIN)
{
// It appears to be a swipe.
if(isProcessingListMove) {
// ignore move, we're currently processing the swipe
return;
}
if (mystartTouchPosition.x < currentTouchPosition.x) {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:0];
return;
}
else {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:2];
return;
}
}
else if(abs(diffy / diffx) > 1)
{
isProcessingListMove = YES;
[super touchesMoved:touches withEvent:event];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
isProcessingListMove = NO;
[super touchesEnded:touches withEvent:event];
}
I'd like to have the same gesture do the same thing on a uiwebview I have set up under the view of the viewcontroller. In short, the uiwebview is on the view. When I swipe, I just get the basic uiwebview functions (scrolling). If I set up a custom uiwebview class with the same code as the uiviewcontroller, I get errors. I need to know how to translate that block of code, keeping the functionality intact, to suit the uiwebview.

You should be able to subclass UIWebView and override those methods. You may have to override all of the touch methods, though,
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
EDIT:
Ah, I see your errors now. They are because those things don't apply to the UIWebView like they did to the parent (which you already know...)
Since UIWebView is a UIView, you should be able to replace
[touch locationInView:self.view]
with
[touch locationInView:self]
To get to the parent's tab bar controller, though, you'll have to design a different way to do it. You could pass a reference to the parent to your UIWebView subclass and expose a method on the parent for changing which tab is active, for instance:
In your subclass, add a property for the parent tab bar controller:
UITabBarController *parent;
When creating your UIWebView subclass and adding it to the tab bar controller, also set the parent:
webView.parent = self;
If you're setting this up in Interface Builder, make the parent an IBOutlet and hook it up through IB.
In your tab bar controller view, add a method that changes the selected controller. You could use named constants for the view you want to switch to, or methods that describe exactly what you are switching to, but for simplicity, you'd do something conceptually similar this:
- (void) switchToView: (int)viewNumber
{
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:viewNumber];
}
then call [parent switchToView:2] in your code.
Another and arguably better way is to use NSNotifications, registering the parent as a listener.

My full code is the following:
#import "CustomWebView.h"
#implementation CustomWebView
//Swipe between tabs
#define HORIZ_SWIPE_DRAG_MIN 100
CGPoint mystartTouchPosition;
BOOL isProcessingListMove;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint newTouchPosition = [touch locationInView:self.view]; "error: request for member 'view' in something not a structure or union"
if(mystartTouchPosition.x != newTouchPosition.x || mystartTouchPosition.y != newTouchPosition.y) {
isProcessingListMove = NO;
}
mystartTouchPosition = [touch locationInView:self.view]; "error: request for member 'view' in something not a structure or union"
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint currentTouchPosition = [touch locationInView:self.view]; "error: request for member 'view' in something not a structure or union"
// If the swipe tracks correctly.
double diffx = mystartTouchPosition.x - currentTouchPosition.x + 0.1; // adding 0.1 to avoid division by zero
double diffy = mystartTouchPosition.y - currentTouchPosition.y + 0.1; // adding 0.1 to avoid division by zero
if(abs(diffx / diffy) > 2.5 && abs(diffx) > HORIZ_SWIPE_DRAG_MIN)
{
// It appears to be a swipe.
if(isProcessingListMove) {
// ignore move, we're currently processing the swipe
return;
}
if (mystartTouchPosition.x < currentTouchPosition.x) {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:0]; "error: request for member 'tabBarController' in something not a structure or union"
return;
}
else {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:2]; "error: request for member 'tabBarController' in something not a structure or union"
return;
}
}
else if(abs(diffy / diffx) > 1)
{
isProcessingListMove = YES;
[super touchesMoved:touches withEvent:event];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
isProcessingListMove = NO;
[super touchesEnded:touches withEvent:event];
}
// End of swipe
#end
The errors are as follows are included with the code.
I appreciate the help.

Related

Pass on a Tap to an UITextView

I was wondering how to pass on a tap on an UIView to an UITextView. This is my code so far:
- (void)foundTap:(UITapGestureRecognizer *)recognizer {
label.text = #"Touch detected";
[self.view bringSubviewToFront:aTextView];
[aTextView touchesBegan:touches withEvent:event];
}
Now, this obviously does not work as touches and event are not defined. But how do I define them? I can't declare touches as 1 (won't work). I could initialise it like so:
UITouch *touches =[touches anyObject];
But then again, touches is still undeclared. And I have no idea of how to declare the event. This is usually easy if you use the - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {} method, but I want to pass on the tap not the touches. Any help would be very much appreciated.
Edit:
I rewrote the method, but I still can't pass on the tap to the UITextView. I now need to double tap in order to edit it, i.e. the first tap for bringing the aTextView upfront and the second tap will then edit the UITextView (as it is in front and thus receives all the touches swipes etc.):
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x); // will always be positive
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y); // will always be positive
if (deltaY == 0 && deltaX == 0) {
label.text = #"Touch"; [self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
[self.view bringSubviewToFront:aTextView];
[self.view bringSubviewToFront:doneEdit];
[aTextView touchesBegan:touches withEvent:event];
}
}
See if this previous SO question iPhone: Detecting Tap in MKMapView helps you.

Converting beginTouches and endTouches to ccBeginTouch and ccEndTouch

I'm converting the game I'm working on from UIkit to coocos2d. Using UIKIT I would use the code below code to pass the touch evens to a method. What would be the equivalent in Cocos2d?
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Save the position
for (UITouch *touch in touches) {
// Send to the dispatch method, which will make sure the appropriate subview is acted upon
[self dispatchFirstTouchAtPoint:[touch locationInView:boardView] forEvent:nil];
}
}
// Saves the first position for reference when the user lets go.
- (void) dispatchFirstTouchAtPoint:(CGPoint)touchPoint forEvent:(UIEvent *)event
{
beginTouchPoint = touchPoint;
}
// Handles the continuation of a touch.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Send to the dispatch method, which will make sure the appropriate subview is acted upon
[self dispatchTouchEndEvent:[touch view] toPosition:[touch locationInView:boardView]];
}
}
-(void) dispatchTouchEndEvent:(UIView *)theView toPosition:(CGPoint)position
{ id sender;
int directionSwiped;
int row,column;
CGFloat xDelta = position.x - beginTouchPoint.x;
CGFloat yDelta = position.y - beginTouchPoint.y;
[self findSwipeDirectionWith: xDelta and: yDelta];
}
What would be the equivalent using cocos2d?
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// Save the position
for (UITouch *touch in touches) {
// Send to the dispatch method, which will make sure the appropriate subview is acted upon
[self dispatchFirstTouchAtPoint:[touch locationInView:boardView] forEvent:nil];
}
}
// Saves the first position for reference when the user lets go.
- (void) dispatchFirstTouchAtPoint:(CGPoint)touchPoint forEvent:(UIEvent *)event
{
beginTouchPoint = touchPoint;
}
// Handles the continuation of a touch.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Send to the dispatch method, which will make sure the appropriate subview is acted upon
[self dispatchTouchEndEvent:[touch view] toPosition:[touch locationInView:boardView]];
}
}
-(void) dispatchTouchEndEvent:(UIView *)theView toPosition:(CGPoint)position
{ id sender;
int directionSwiped;
int row,column;
CGFloat xDelta = position.x - beginTouchPoint.x;
CGFloat yDelta = position.y - beginTouchPoint.y;
[self findSwipeDirectionWith: xDelta and: yDelta];
}
I've tried to figure this out on my own, and I've spent hours on google, but I haven't come up with a workable solution.
I figured it out. I discovered that, what I was forgetting to do, was to add:
self.isTouchEnabled = YES;
to the Layer's init method.
After I did that the following code worked for me (beginTouchPoint and endTouchPoint are properties of the class):
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView: [myTouch view]];
beginTouchPoint = [[CCDirector sharedDirector]convertToGL:location];
}
// Handles the continuation of a touch.*
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
static BOOL isFirstTouch = YES;
UITouch* myTouch = [touches anyObject];
int row,column;
int directionSwiped;
CGPoint location = [myTouch locationInView: [myTouch view]];
endTouchPoint = [[CCDirector sharedDirector]convertToGL:location];
CGFloat xDelta = endTouchPoint.x - beginTouchPoint.x;
CGFloat yDelta = endTouchPoint.y - beginTouchPoint.y;
[self findSwipeDirectionWith: xDelta and: yDelta];
}

UITableView: custom gestures make it scrolling no more

I have an UIViewController which contains a UITableView (subclassed) and another UIView (subclassed). They are on the same hierarchy level but the UIView is added last so it is the frontmost.
I overrid touchesBegan/Moved/Ended to intercept the Gestures from the top UIView: my goal is to get the selected UITableViewCell and, if double tapped, create an ImageView to be dragged around.
I appear to get it done but now I cannot scroll the UITableView anymore, even though I forward the touch events.
Here are the methods for the UIView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BO");
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesBegan:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"Mo");
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
copyObj.center = tPoint;
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesMoved:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
if ([touch tapCount] == 2)
{
[self desubCopyView];
}
CGPoint tPoint = [touch locationInView:self];
copyObj.center = tPoint;
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesEnded:touches withEvent:event];
//[cell.imageView touchesEnded:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesEnded:touches withEvent:event];
}
}
And here are those in the UITableViewCell
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
if ([touch tapCount] == 2)
{
CGPoint tPoint = [touch locationInView:self];
NSLog(#"CellX %lf CY %lf", tPoint.x, tPoint.y);
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView * newView = [[UIImageView alloc] initWithImage:viewImage];
[dragArea addSubview:newView];
dragArea.copyObj = newView;
[newView release];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
dragArea.copyObj.transform = CGAffineTransformMakeScale(1.3, 1.3);
[UIView commitAnimations];
tPoint = [self convertPoint:tPoint toView:dragArea];
dragArea.copyObj.center = tPoint;
}
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"MOV %#", self.imageView.image);
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"ENDED");
[super touchesEnded:touches withEvent:event];
}
And in my UITableView I have simply:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BEGTB");
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"MOVTB");
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"ENDTB");
[super touchesEnded:touches withEvent:event];
}
I am surely missing something but I do not know what
I would highly recommend looking into UITapGestureRecognizer instead of handling the touchesX events yourself.
I found a workaround for this, override the methods for touch gestures in my custom UITableView in order to make it scroll programMatically as I drag upon it an object.
Here is the 'solution'.
I still believe there is another simpler way to do this but I did not find it, so posting this and marking it as an 'answer' might help someone else.

iPhone - Gestures on UIPickerView and UIWebView

I'm making an iPhone app in which the user can do gestures (left and right swipes) to flick through tabs. My problem is that some of the pages have views such as the pickerview, webview, textfields and buttons. The swipes don't work on these. Is there any way to have global gestures?
For reference, my gesture code example:
//Swipe between tabs
#define mindrag 100
CGPoint mystartTouchPosition;
BOOL isProcessingListMove;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint newTouchPosition = [touch locationInView:self.view];
if(mystartTouchPosition.x != newTouchPosition.x || mystartTouchPosition.y != newTouchPosition.y) {
isProcessingListMove = NO;
}
mystartTouchPosition = [touch locationInView:self.view];
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint currentTouchPosition = [touch locationInView:self.view];
// If the swipe tracks correctly.
double diffx = mystartTouchPosition.x - currentTouchPosition.x + 0.1; // adding 0.1 to avoid division by zero
double diffy = mystartTouchPosition.y - currentTouchPosition.y + 0.1; // adding 0.1 to avoid division by zero
if(abs(diffx / diffy) > 2.5 && abs(diffx) > mindrag)
{
// It appears to be a swipe.
if(isProcessingListMove) {
// ignore move, we're currently processing the swipe
return;
}
if (mystartTouchPosition.x < currentTouchPosition.x) {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:1];
return;
}
else {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:3];
return;
}
}
else if(abs(diffy / diffx) > 1)
{
isProcessingListMove = YES;
[super touchesMoved:touches withEvent:event];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
isProcessingListMove = NO;
[super touchesEnded:touches withEvent:event];
}
// End of swipe
Any input is appreciated.
You can subclass UIImagePickerController and UIWebView and add these gesture capturing views to them, so long as you gesture detection can still pass touches to the underlying views of course.

Successful Swipe in UITextView?

Has anyone been able to implement a successful swipe gesture on a UITableView? By default this is not possible due to the scrolling nature of the control. I've tried subclassing UITextView and implementing the swipe function in the instance methods, but no dice.
My UITextView has scrolling disabled - Unless there is another way to implement multiline text?
[Edit] What I should say is input multiline text[/Edit]
Here is a subclass of a UITextView that will detect a swipes gesture...
Is this what you are looking for?
#import <UIKit/UIKit.h>
#define kMinimumGestureLength 25
#define kMaximumVariance 5
#interface SwipeableTextView : UITextView {
CGPoint gestureStartPoint;
}
#end
#implementation SwipeableTextView
- (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];
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
NSLog(#"Horizontal swipe detected");
}
}
#end
An even better solution, I have found, is to simply pass touches back to the superview:
#interface SwipeableTextView : UITextView {
}
#end
#implementation SwipeableTextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.superview touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self.superview touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self.superview touchesEnded:touches withEvent:event];
}
#end
Add a transparent UIView on top of the UITextView and use it to handle the swipe, and send the touchesBegan/Moved/Ended/Canceled: messages to the text view to preserve normal interaction.
I took Ross's code and added a few things that helped me. In particular, this code won't respond to the swipe until it stops. It also prevents a swipe from reversing direction partway through.
#import <UIKit/UIKit.h>
#define kMinimumGestureLength 25
#define kMaximumVariance 5
typedef enum swipeDirection {
kSwipeNone,
kSwipeLeft,
kSwipeRight
} tSwipeDirection;
#interface SwipeableTextView : UITextView {
CGPoint gestureStartPoint;
tSwipeDirection swipeDirection;
}
#end
#implementation SwipeableTextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
swipeDirection = kSwipeNone;
UITouch *touch =[touches anyObject];
gestureStartPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
// Check if we already started a swipe in a particular direction
// Don't let the user reverse once they get going
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance &&
swipeDirection == kSwipeNone) {
if (gestureStartPoint.x < currentPosition.x) {
swipeDirection = kSwipeRight;
}
else {
swipeDirection = kSwipeLeft;
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (swipeDirection == kSwipeRight) {
NSLog(#"Swipe right");
}
else if (swipeDirection == kSwipeLeft) {
NSLog(#"Swipe left");
}
[super touchesEnded:touches withEvent:event];
}
#end
Usually you implement the touch responding events in a UITableViewCell, which can recognize the swipe and do something. UITableViews inherit from UIScrollView which does not like having touch events overridden.
You could also use a two finger swipe. When I get home I can elaborate with some code but that was what I have used