So I'm trying to create an app that changes a label based on the direction you swipe. However, instead of being able to swipe anywhere on the screen I'd like it to only swipe on a specific view. I put a new view on the .xib file and used that view for the location of touches and named it leftView, but it still uses the whole screen. I'm using a touchesBegan and touchesMoved method so I can use diagonals.
Here's a bit of the code I'm using:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:leftView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:leftView];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
if ((deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) && currentPosition.x < gestureStartPoint.x){
label.text = #"Left Horizontal swipe detected";
}
else if((deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) && currentPosition.x > gestureStartPoint.x){
label.text = #"Right Horizontal swipe detected";
}
else if ((deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) && currentPosition.y < gestureStartPoint.y){
label.text = #"Up Vertical swipe detected";
}
else if ((deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) && currentPosition.y > gestureStartPoint.y){
label.text = #"Down Vertical swipe detected";
}
You can achieve what you're after by adding two UIGestureRecognizers (one for right swipe and another for left swipe) to the UIView that you want to receive the swipes.
Apple's SimpleGestureRecognizer sample project gives a great illustration of how to set this up.
Check the identity inspector whether the uiview class is selected for the UIView object in organizer. I guess u have implemented the touchesBegan method in the sub class of UIView.
Related
I have a scroll view with a fixed number of thumbnail images added as subview. I want to move these images along touch to the rectangle in another view. I am able to move the image along scrollview (ie, along the same view) but not able to move across another view.
Now am changing the center of image depending on touch position. When touch point increases beyond the frame of scrollview the image disappears. This is my problem
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView: self];
NSLog(#"%f,%f",location.x,location.y);
touch.view.center = location;
}
Any solution to this problem will be a great help for me !!!
Please refer image for further details
Here is what I'd do :
Add a panGestureRecognizer to each image, with the following handlePan: method as its action. You still have to figure out how to get the correct imageView (myImageView), but this will make your image follow your finger.
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
// Find the correct image you're dragging
// UIImageView *myImageview = ...
CGPoint translation = [recognizer translationInView:self.view];
if (recognizer.state == UIGestureRecognizerStateEnded) {
// What to do when you start the gesture
// You may also define myImageView here
// (if so, make sure you put it in #interface,
// because this method will be called multiple times,
// but you will enter this "if" only when you start the touch)
}
// Keep your image in the screen
if (myImageView.frame.origin.x + translation.x >= 0.0f &&
myImageView.frame.origin.x + myImageView.frame.size.width + translation.x <= 320.0f &&
myImageView.frame.origin.y + translation.y >= 0.0f &&
myImageView.frame.origin.y + myImageView.frame.size.height + translation.y <= 480.0f) {
myImageView.center = CGPointMake(myImageView.center.x + translation.x,
myImageView.center.y + translation.y);
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
// What to do when you remove your finger from the screen
}
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
I'm quite new to iPhone development and I'm building my first app :)
In one of my view controllers I built a customSlider that should behave as the native "slide to unlock" slider.
My doubt right now is how to implement the "drag outside" behaviour. As said, I would like to have it exactly as the native slider, that means that when the finger is dragging the slider if it moves out the slider, the slider should be going to zero.
My doubt is not on the animation part (I'm already using animation block successfully), but on control events part. Which control event should I use?
I'm using:
[customSlider addTarget:self action:#selector(sliderMoved:) forControlEvents:UIControlEventValueChanged];
to handle the sliding part (finger sliding the cursor), and
[customSlider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventTouchUpInside];
to handle the release part, but the problem is that if I release the finger outside, the sliderAction function it's not called.
EDIT:
I've tried to implement the solution #Bruno Domingues gave me, but I'm realizing that the issue is that by default UISlider keep getting updated even if the finger is dragged outside of it (try to open for example the Brightness section in System Preferences and you'll see that the slider will keep on updating even if you drag outside of it). So my question could be redefined: How to avoid this default behaviour and have the slider updating only when the finger is moving on it?
Simply interrupt the touches methods in your custom subclass and only forward the touches you want acted on to the superclass, like so:
in .h:
#interface CustomSlider : UISlider
#end
in .m:
#import "CustomSlider.h"
#implementation CustomSlider
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint touchLocation = [[touches anyObject] locationInView:self];
if (touchLocation.x < 0 || touchLocation.y<0)return;
if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint touchLocation = [[touches anyObject] locationInView:self];
if (touchLocation.x < 0 || touchLocation.y<0)return;
if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
[super touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint touchLocation = [[touches anyObject] locationInView:self];
if (touchLocation.x < 0 || touchLocation.y<0)return;
if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
[super touchesEnded:touches withEvent:event];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint touchLocation = [[touches anyObject] locationInView:self];
if (touchLocation.x < 0 || touchLocation.y<0)return;
if (touchLocation.x > self.bounds.size.width || touchLocation.y > self.bounds.size.height)return;
[super touchesCancelled:touches withEvent:event];
}
#end
Please note that this implementation will start updating the control if your finger moves back to the control. To eliminate this, simply set a flag if a touch is received outside of the view then check that flag in subsequent touches methods.
You need to set a [slider addTarget:self action:#selector(eventDragOutside:) forControlEvents:UIControlEventTouchDragOutside];
And add a function
- (IBAction)eventDragOutside:(UISlider *)sender {
sender.value = 0.0f;
}
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");
}
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.
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.
I have a view control and inside I plan to place some controls like buttons textbox etc... I can drag my view along the x axis like:
1)
2)
with the following code:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if( [touch view] == ViewMain)
{
CGPoint location = [touch locationInView:self.view];
displaceX = location.x - ViewMain.center.x;
displaceY = ViewMain.center.y;
startPosX = location.x - displaceX;
}
CurrentTime = [[NSDate date] timeIntervalSince1970];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if( [touch view] == ViewMain)
{
CGPoint location = [touch locationInView:self.view];
location.x =location.x - displaceX;
location.y = displaceY;
ViewMain.center = location;
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
double time = [[NSDate date] timeIntervalSince1970]-CurrentTime;
UITouch *touch = [[event allTouches] anyObject];
if( [touch view] == ViewMain)
{
CGPoint location = [touch locationInView:self.view];
location.x =location.x - displaceX;
location.y = displaceY;
ViewMain.center = location;
double speed = (ViewMain.center.x-startPosX)/(time*2);
NSLog(#"speed: %f", speed);
}
}
not that I have to add the global variables:
float displaceX = 0;
float displaceY = 0;
float startPosX = 0;
float startPosY = 0;
double CurrentTime;
the reason why I created those variables is so that when I start dragging the view the view moves from the point where I touch it instead of from the middle.
Anyways if I touch a button or image the view will not drag even though the images have transparency on the background. I want to be able to still be able to drag the view regardless if there is an image on top of the view. I where thinking that maybe I need to place a large transparent view on top of everything but I need to have buttons, images etc. I want to be able to drag a view just like you can with:
note that I was able to drag the view regardless of wither I first touched an app/image or text. How could I do that?
I think your problem is that if you touch a UIButton or a UIImageView with interaction enabled, it doesn't pass the touch along.
For the images, uncheck the User Interaction Enabledproperty in IB.
For the buttons that are causing touchesBegan:withEvent:, etc. to not get called, then look at the following link: Is there a way to pass touches through on the iPhone?.
You may want to consider a different approach to this problem. Rather than trying to manually manage the content scrolling yourself you would probably be better off using a UIScrollView with the pagingEnabled property set to YES. This is the method Apple recommends (and it's probably the method used by Springboard.app in your last screenshot). If you are a member of the iOS developer program check out the WWDC 2010 session on UIScrollView for an example of this. I think they may have also posted sample code on developer.apple.com.
Simple question here: How can I detect when I user swipes their finger on the screen of the iPhone?
You need to implement a gesture recognizer in your application.
In your interface:
#define kMinimumGestureLength 30
#define kMaximumVariance 5
#import <UIKit/UIKit.h>
#interface *yourView* : UIViewController {
CGPoint gestureStartPoint;
}
#end
kMinimumGestureLength is the minimum distance the finger as to travel before it counts as a swipe. kMaximumVariance is the maximum distance, in pixels, that the finger can end above the beginning point on the y-axis.
Now open your interface .xib file and select your view in IB, and make sure Multiple Touch is enabled in View Attributes.
In your implementation, implement these methods.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
if(deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance){
//do something
}
else if(deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance){
//do something
}
}
This is one way to implement a swipe recognizer. Also, you really should check out the Docs on this topic:
UISwipeGestureRecognizer
The UIGestureRecognizer is what you want. Especially the UISwipeGestureRecognizer subclass
Ah, I was able to answer my own question: http://developer.apple.com/library/ios/#samplecode/SimpleGestureRecognizers/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009460
Thanks for the help everyone!