i'm trying to move a UIView with relation to the user's touches.
Here's what I have at the moment:
int oldX, oldY;
BOOL dragging;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(window.frame, touchLocation)) {
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(window.frame, touchLocation) && dragging) {
CGRect frame;
frame.origin.x = (window.frame.origin.x + touchLocation.x - oldX);
frame.origin.y = (window.frame.origin.y + touchLocation.y - oldY);
window.frame = frame;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
dragging = NO;
}
The view keeps flickering from one location to another, and I don't know what else to do.
Any help appreciated.
What you want is to use a UIPanGestureRecognizer, introduced in iOS 3.2. You use it with something as easy as this (from your UIViewController subclass):
-(void)viewDidLoad;
{
[super viewDidLoad];
UIPanGestureRecognizer* pgr = [[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(handlePan:)];
[self.panningView addGestureRecognizer:pgr];
[pgr release];
}
-(void)handlePan:(UIPanGestureRecognizer*)pgr;
{
if (pgr.state == UIGestureRecognizerStateChanged) {
CGPoint center = pgr.view.center;
CGPoint translation = [pgr translationInView:pgr.view];
center = CGPointMake(center.x + translation.x,
center.y + translation.y);
pgr.view.center = center;
[pgr setTranslation:CGPointZero inView:pgr.view];
}
}
Modify the touchesBegan and touchesMoved methods to be like the following.
float oldX, oldY;
BOOL dragging;
The touchesBegan:withEvent: method.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(window.frame, touchLocation)) {
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
The touchesMoved:withEvent: method.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (dragging) {
CGRect frame = window.frame;
frame.origin.x = window.frame.origin.x + touchLocation.x - oldX;
frame.origin.y = window.frame.origin.y + touchLocation.y - oldY;
window.frame = frame;
}
}
The touchesEnded:withEvent: method.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
dragging = NO;
}
Here is code in Swift, a slightly more generalist version that works with an ImageView that has been created on the screen.
let panGestureRecongnizer = UIPanGestureRecognizer(target:self, action: "handlepan:")
imageView.addGestureRecognizer(panGestureRecongnizer)
func handlepan(sender: UIPanGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Changed) {
var center = sender.view?.center
let translation = sender.translationInView(sender.view)
center = CGPointMake(center!.x + translation.x, center!.y + translation.y)
sender.view?.center = center!
sender .setTranslation(CGPointZero, inView: sender.view)
}
}
//Above answer in Swift 4.0
var dragging: Bool = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = event?.allTouches?.first
let touchPoint = touch?.location(in: self.window)
i
if viewToDrag.frame.contains(touchPoint!){
dragging = true
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = event?.allTouches?.first
let touchPoint = touch?.location(in: self.window)
if (dragging) {
viewToDrag.center = CGPoint(x: (touchPoint?.x)!, y: (touchPoint?.y)!)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
dragging = false
}
#PeyLoW answer is the answer I suggest and add limit to parent boundaries
-(void)handlePan:(UIPanGestureRecognizer*)pgr;
{
if (pgr.state == UIGestureRecognizerStateChanged) {
CGPoint center = pgr.view.center;
CGPoint translation = [pgr translationInView:pgr.view];
center = CGPointMake(center.x + translation.x,
center.y + translation.y);
if ([self pointInside:center withEvent:nil])
{
pgr.view.center = center;
[pgr setTranslation:CGPointZero inView:pgr.view];
}
}
}
Related
I am developing one application. In that I am using one UIImageView and I am changing the position of the UIImageView every 0.5 seconds using below code.
[NSTimer scheduledTimerWithTimeInterval:0.5
target: self
selector:#selector(moveImage)
userInfo: nil repeats:YES];
-(void) moveImage
{
//[image1 setCenter: CGPointMake(634, 126)];
CGFloat x = (CGFloat) (arc4random() % (int) self.view.bounds.size.width);
CGFloat y = (CGFloat) (arc4random() % (int) self.view.bounds.size.height);
CGPoint squarePostion = CGPointMake(x, y);
img.center=squarePostion;
}
Now i can touch the screen. What i need to find out is my touch location and that imageview location both are correct or not.
use this
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if ([touch view] == photo1) {
//photo1 is image view give tag to it
if( photo1.tag==3)
{
NSLog(#"You have been touched image view");
}
photo1.center = touchLocation;
}
}
//it used to find the point in view
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [touches anyObject];
CGPoint pos = [touch locationInView: [UIApplication sharedApplication].keyWindow];
NSLog(#"%f,%f",pos.x, pos.y);
}
I have a UIView and on the top of him I've got UIButton, UITableView and UINavigationBar. what I want to do is, when you click on the UIButton and drag it, all the view will move to the left side only, until the screen ends. I mean that you can drag the view to the left and when you stop touching, the view will stop where your finger stopped touching the screen.
I can't do it, he shows me only the "Touches Began 1" and thats it. what seems to be my problem here?
Thanks alot everyone!
float oldX, oldY;
BOOL dragging;
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
NSLog("Touches Began 1");
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(btnOpen.frame, touchLocation))
{
NSLog("Touches Began 2");
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (dragging)
{
NSLog("Dragging!");
CGRect frame = mainView.frame;
frame.origin.x = mainView.frame.origin.x + touchLocation.x - oldX;
frame.origin.y = mainView.frame.origin.y + touchLocation.y - oldY;
mainView.frame = frame;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog("Touches Ended!");
dragging = NO;
}
- (void)viewDidLoad
{
[btnOpen addTarget:self action:#selector(touchesBegan:withEvent:) forControlEvents: UIControlEventTouchDown];
[btnOpen addTarget:self action:#selector(touchesMoved:withEvent:) forControlEvents: UIControlEventTouchDragInside];
[btnOpen addTarget:self action:#selector(touchesEnded:withEvent:) forControlEvents: UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
}
You are asking for the coordinates of the touch in relation to the frame of main view
CGPoint touchLocation = [touch locationInView:self.view];
And then you ask if the location of the touchLocation is within the frame of the buttons frame
if (CGRectContainsPoint(btnOpen.frame, touchLocation))
In another words. You are checking if the touch was within the bounds of the button but you check by the coordinates of the touch made on the main view instead of the button.
So this is what you should do:
CGPoint touchLocation = [touch locationInView:btnOpen];
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
NSLog("Touches Began 1");
for (UITouch *touch in touches) {
//UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(btnOpen.frame, touchLocation))
{
NSLog("Touches Began 2");
//dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
// UITouch *touch = [[event allTouches] anyObject];
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(btnOpen.frame, touchLocation))
{
NSLog("Dragging!");
CGRect frame = mainView.frame;
frame.origin.x = mainView.frame.origin.x + touchLocation.x - oldX;
frame.origin.y = mainView.frame.origin.y + touchLocation.y - oldY;
mainView.frame = frame;
}
}
}
Try like this i think it will be helpful to you.
Im trying to get my sprite to move on touch but seems to disappear on touch then reappear on second touch . I do not know how to fix this to get my sprite to move at the direction I tap. I have been trying to figure this out for a while but seems I am out of luck. I am hoping someone can point me at the right direction.
CGSize winSize = [[CCDirector sharedDirector] winSize];
player = [CCSprite spriteWithFile:#"Player.png"
rect:CGRectMake(0, 0, 27, 40)];
player.position = ccp(player.contentSize.width/2, winSize.height/2);
[self addChild:player z:1];
(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:YES];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
-(void)setPlayerPosition:(CGPoint)position {
player.position = position;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint playerPos = player.position;
CGPoint diff = ccpSub(touchLocation, playerPos);
if (abs(diff.x) > abs(diff.y)) {
if (diff.x > 0) {
playerPos.x += contentSize_.width;
} else {
playerPos.x -= contentSize_.width;
}
} else {
if (diff.y > 0) {
playerPos.y += contentSize_.height;
} else {
playerPos.y -= contentSize_.height;
}
}
The syntax of your touch function seems to be different.
try this code instead
- (void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
CGPoint playerPos = player.position;
}
I have added one label in imageview and added Pangesture for dragging label.With this code label is dragged within the whole.but i want to restrict the label drag within the frame of uiimageview only.
How can i achieve this? The code for adding lable and gesture is as below.
lblQuote=[[UILabel alloc]init];
lblQuote.frame=CGRectMake(130,380, 400,100);
lblQuote.userInteractionEnabled=TRUE;
lblQuote.backgroundColor=[UIColor clearColor];
lblQuote.userInteractionEnabled=TRUE;
lblQuote.lineBreakMode=UILineBreakModeWordWrap;
lblQuote.font=[UIFont systemFontOfSize:40.0];
lblQuote.tag=500;
lblQuote.text=#"";
CGSize maximumLabelSize = CGSizeMake(296,9999);
CGSize expectedLabelSize = [strQuote sizeWithFont:lblQuote.font
constrainedToSize:maximumLabelSize
lineBreakMode:lblQuote.lineBreakMode];
//adjust the label the the new height.
int nooflines=expectedLabelSize.height/16.0;
lblQuote.numberOfLines=nooflines;
// CGRect newFrame = lblQuote.frame;
//newFrame.size.height = expectedLabelSize.height;
// yourLabel.frame = newFrame;
lblQuote.textAlignment=UITextAlignmentCenter;
UIPanGestureRecognizer *gesture = [[[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(labelDragged:)] autorelease];
[lblQuote addGestureRecognizer:gesture];
- (void)labelDragged:(UIPanGestureRecognizer *)gesture
{
UILabel *label = (UILabel *)gesture.view;
CGPoint translation = [gesture translationInView:label];
// move label
label.center = CGPointMake(label.center.x + translation.x,
label.center.y + translation.y);
// reset translation
[gesture setTranslation:CGPointZero inView:label];
}
I tried moving label on touches event.code is as below:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint pointMoved = [touch locationInView:imgView2];
lblQuote.frame=CGRectMake(pointMoved.x, pointMoved.y, lblQuote.frame.size.width, lblQuote.frame.size.height);
}
But movement is not as smooth as pan gesture recognizer.sometimes touch is in another direction and label movement in some other direction and sometimes more or less movement than touch.
Well this is totally depends on your logics although i wrote a code for you, hope this will help you!!
Make sure your UILabel UserIntraction is set to be YES; and use touch began and touch moved methods instead of pangesture.... see this now
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
if([touch view] == lblName)
{
NSLog(#"touch on label");
CGPoint pt = [[touches anyObject] locationInView:lblName];
startLocation = pt;
// startLocation is a CGPoint declare globaly in .h..and lblName is your UILabel
}
}
- (void) touchesMoved:(NSSet *)touches withEvent: (UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
if([touch view] == lblName)
{
CGPoint pt = [[touches anyObject] previousLocationInView:lblName];
CGFloat dx = pt.x - startLocation.x;
CGFloat dy = pt.y - startLocation.y;
CGPoint newCenter = CGPointMake(lblName.center.x + dx, lblName.center.y + dy);
//now put if condition for dragging in specific area
CGFloat min_X = lblName.center.x + dx - lblName.frame.size.width/2.0;
CGFloat max_X = lblName.center.x + dx + lblName.frame.size.width/2.0;
CGFloat min_Y = lblName.center.y + dy - lblName.frame.size.height/2.0;
CGFloat max_Y = lblName.center.y + dy + lblName.frame.size.height/2.0;
if((min_X >= imageView.frame.origin.x && max_X <= imageView.frame.origin.x + imageView.frame.size.width) && (min_Y >= imageView.frame.origin.y && max_Y <= imageView.frame.origin.y + imageView.frame.size.height))
{
lblName.center = newCenter;
}
}
}
I was wondering if there is an easy method to set the minimum swipe length, i.e. the length in pixel the user needs to swipe so that the gesture is recognised as a swipe.
I noticed that the normal swipe can be quite unresponsive (as compared to swiping photos in your photo library for instance).
This is the normal way, but I would like to reduce the required length of the swipe:
- (void)viewDidLoad
{
// SWIPING GESTURES:
UISwipeGestureRecognizer *swipeLeftRecognizer;
swipeLeftRecognizer=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(foundLeftSwipe:)];
swipeLeftRecognizer.direction=UISwipeGestureRecognizerDirectionLeft;
//swipeRecognizer.numberOfTouchesRequired=1;
[self.view addGestureRecognizer:swipeLeftRecognizer];
[swipeLeftRecognizer release];
UISwipeGestureRecognizer *swipeRightRecognizer;
swipeRightRecognizer=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(foundRightSwipe:)];
swipeRightRecognizer.direction=UISwipeGestureRecognizerDirectionRight;
//swipeRecognizer.numberOfTouchesRequired=1;
[self.view addGestureRecognizer:swipeRightRecognizer];
[swipeRightRecognizer release];
[super viewDidLoad];
}
#pragma mark -
#pragma mark Swipes
- (void)foundLeftSwipe:(UISwipeGestureRecognizer *)recognizer {
// do something
}
- (void)foundRightSwipe:(UISwipeGestureRecognizer *)recognizer {
// do something
}
I remember that there is a way to get the pixel start position and end position and then compare the two, but was just wondering if there is a simpler method, i.e. by simply defining a value for the minimum required swipe length in the code I have here.
EDIT:
This is how I recoded the whole thing:
- (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 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
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
if (deltaXX > 0) {
label.text = #"Horizontal Left swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
else {
label.text = #"Horizontal Right swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
}
if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
if (deltaYY > 0) {
label.text = #"Vertical up swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
else {
label.text = #"Vertical down swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
}
}
I was facing the exact same thing, and I was looking for a solution. There is no such thing as required distance in UISwipeGestureRecognizer but that's not all you can use. I ended up using UIPanGestureRecognizer! It looks like a super class of swipe gesture recognizer and it certainly exposes more useful data.
There is a method available in this class called translationInView:(UIView *)aView the returns a CGPoint where x & y is the total distance over the whole pan gesture! something amazingly useful for our request! And it respects the direction by positive and negative values, meaning: y=+100 -> swipe up to down 100 points, x=+100 swipe left to right 100 points, etc...
What am I doing is to check if the user has panned for a given amount of points (I was looking for something like swipe down, so for me it was y>150) and then do my thing...
here is some code excerpts:
-(void)viewDidLoad
{
UIPanGestureRecoginzer *panDown=[[UIPanGestureRecoginzer alloc] initWithTarget:self action:#selector(swipedDown:)];
[self.view addGestureRecognizer:panDown];
}
.
.
.
-(void)swipedDown:(UIPanGestureRecoginzer *)recognizer
{
CGPoint panned=[recognizer translationInView:self.view];
if(panned.y>150){
//swiped down for 150 points
doSomething();
}
}
It also gives you the velocity of the pan: velocityInView:
Note: You should use your superview (self.view in most cases) if you want to have an overall pan effect.
Hope this helps: UIPanGestureRecognize Class Reference
Please see this documentation http://developer.apple.com/library/ios/#documentation/uikit/reference/UIResponder_Class/Reference/Reference.html
You can use touchesBegan, touchesMoved, and touchesEnded methods to find the start/end positions of the swipe. By finding the delta between the start and the end, you can get the swipe length.
Maybe something like this.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint gestureEndPoint = [touch locationInView:self.view];
// compare gestureStartPoint and gestureEndPoint to determine swipe length
}
Check out this question: UISwipeGestureRecognizer Swipe length
Swift 3:
var gestureStartPoint: CGPoint = .zero
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
gestureStartPoint = touch!.location(in: self.view)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let gestureEndPoint = touch!.location(in: self.view)
let dist = distance(gestureStartPoint, gestureEndPoint)
print(dist)
}
func distance(_ a: CGPoint, _ b: CGPoint) -> CGFloat {
let xDist = a.x - b.x
let yDist = a.y - b.y
return CGFloat(sqrt((xDist * xDist) + (yDist * yDist)))
}