I am trying to drag an image view. I have got little bit of success in doing so, but it is not behaving as i want. I wish that it should move only if touch inside the image and drag it.
But it is moving even if I am touching and dragging from anywhere on the screen.
I have written code like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//retrieve touch point
CGPoint pt= [[ touches anyObject] locationInView:[self.view.subviews objectAtIndex:0]];
startLocation = pt;
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView: [self.view.subviews objectAtIndex:0]];
CGRect frame = [[self.view.subviews objectAtIndex:0]frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[[self.view.subviews objectAtIndex:0] setFrame: frame];
}
The return value of locationInView method is point relative to the view frame. check it is or not in the view frame first.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect targetFrame = [self.view.subviews objectAtIndex:0].frame;
//retrieve touch point
CGPoint pt= [[ touches anyObject] locationInView:[self.view.subviews objectAtIndex:0]];
//check if the point in the view frame
if (pt.x < 0 || pt.x > targetFrame.size.width || pt.y < 0 || pt.y > targetFrame.size.height)
{
isInTargetFrame = NO;
}
else
{
isInTargetFrame = YES;
startLocation = pt;
}
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
if(!isInTargetFrame)
{
return;
}
//move your view here...
}
Try something like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//retrieve touch point
startLocation = [[ touches anyObject] locationInView:self.view];
// Now here check to make sure that start location is within the frame of
// your subview [self.view.subviews objectAtIndex:0]
// if it is you need to have a property like dragging = YES
// Then in touches ended you set dragging = NO
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView: [self.view.subviews objectAtIndex:0]];
CGRect frame = [[self.view.subviews objectAtIndex:0]frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[[self.view.subviews objectAtIndex:0] setFrame: frame];
Related
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;
}
}
}
structure
UIViewController
- UIScrollview
- UIButton
I'm going to make scrollview can receive button event. So whenever user do scrolling(dragging) on the button, the scrollview react as scrolling itself.
I made button down and moved handler with event forwarding like below
- (IBAction)buttonTouchedMove:(id)sender withEvent:(UIEvent *)event {
[[sender nextResponder] touchesMoved:[event allTouches] withEvent:event];
}
- (IBAction)buttonTouchedDown:(id)sender withEvent:(UIEvent *)event {
[[sender nextResponder] touchesBegan:[event allTouches] withEvent:event];
}
and to move scrollview as touches change, I made below codes
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
self.oldPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint offset = self.scrollView1.contentOffset;
UITouch *touch = [touches anyObject];
self.newPoint = [touch locationInView:self.view];
int diffX = newPoint.x - oldPoint.x;
offset.x = offset.x - diffX;
[scrollView1 setContentOffset:offset animated:YES];
self.oldPoint = self.newPoint;
}
but, scrollview react strange.. not move enough as I touch moved.
// get the button width
CGFloat buttonWidth = self.theButton.frame.size.width;
// replace your button name here
// now get the view width
CGFloat viewWidth = self.view.frame.size.width;
// replace your view name here
// now get the multiplier which i said u as 10
CGFloat mult = viewWidth / buttonWidth;
// and now use it in your code
.
.
.
diffX = mult*(newPoint.x - oldPoint.x);
.
.
.
Final answer, Using UIView animation, I got what i want.
- (IBAction)buttonTouchedDown:(id)sender withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
self.oldPoint = [touch locationInView:self.view];
self.velocity = 0;
isButtonTouchedDown = YES;
}
- (IBAction)buttonTouchedMove:(id)sender withEvent:(UIEvent *)event {
CGPoint offset = self.scrollView1.contentOffset;
UITouch *touch = [[event allTouches] anyObject];
self.newPoint = [touch locationInView:self.view];
int diffX = self.newPoint.x - self.oldPoint.x;
velocity = diffX;
offset.x = offset.x - diffX;
[self.scrollView1 setContentOffset:offset animated:NO];
self.oldPoint = self.newPoint;
}
There is a trick..I calculated velocity by differentiating moved distance in TouchedMove function.
In Button TouchUp event handler, I controlled Scrollview with velocity value and CurveEaseout animation to scroll more. By offering very short duration of animation and repeating it if there is no event(button touched down). It becomes very similar with scrollview's animation.
- (IBAction)buttonTouchedUp:(id)sender withEvent:(UIEvent *)event {
CGPoint offset = self.scrollView1.contentOffset;
amountX = (self.velocity)*(abs(self.velocity));
dX = amountX;
isButtonTouchedDown = NO;
if (offset.x - amountX < 0) {
offset.x = 0;
[scrollView1 setContentOffset:offset animated:YES];
}
else if (abs(dX) < 70) { // 70: content item size
offset.x = roundf(offset.x/70)*70;
[scrollView1 setContentOffset:offset animated:YES];
}
else {
[self endScrollAnimation];
}
- (void)startAnimation:(float)x moveAmount:(float)moveAmount
{
CGPoint offset = self.scrollView1.contentOffset;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
offset.x = x < 0 ? offset.x + moveAmount : offset.x -moveAmount;
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(endScrollAnimation)];
[scrollView1 setContentOffset:offset animated:NO];
[UIView commitAnimations];
}
- (void)endScrollAnimation
{
CGPoint offset = self.scrollView1.contentOffset;
float slowMoveAmount = abs(amountX) * 0.05;
float fastMoveAmount = abs(amountX) * 0.1;
if (isButtonTouchedDown) {
return;
}
else if (abs(dX) > abs(amountX)*0.35 && offset.x > 0) {
[self startAnimation:dX moveAmount:fastMoveAmount];
dX = dX < 0 ? dX + fastMoveAmount : dX -fastMoveAmount;
}
else if (abs(dX) > slowMoveAmount && offset.x > 0) {
[self startAnimation:dX moveAmount:slowMoveAmount];
dX = dX < 0 ? dX + slowMoveAmount : dX - slowMoveAmount;
}
else {
offset.x = roundf(offset.x/70)*70;
[scrollView1 setContentOffset:offset animated:YES];
}
}
I have a project where a UITextView (for multilines) can be dragged around the screen. So far my solution to this has been an overlay of an invisible UIButton which when dragged its center is the same as the UITextView's center.
However I've seen apps that seem to just allow the UITextView to be dragged and edited on the fly so it seems there might not be an overlay in those but I'm not sure.
Thoughts?
By the way, c in this code is the UIButton and this is how I have moved it thus far:
- (void) draggedOut: (UIControl *) c withEvent: (UIEvent *) ev
{
if(self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
}
- (void)panTextView:(UIPanGestureRecognizer *)recognizer {
NSLog(#"panning");
location1 = [recognizer translationInView:draggableTextView];
recognizer.view.center = CGPointMake(recognizer.view.center.x + location1.x,
recognizer.view.center.y + location1.y);
[recognizer setTranslation:CGPointMake(0,0) inView:draggableTextView];
location1 =[recognizer locationInView:draggableTextView];
NSLog(#"tranlation %#",NSStringFromCGPoint(location1));
[_imgpic addSubview:recognizer.view];
appDelegate.txt=draggableTextView.text;
}
call this method after creating textview.
Well have not been able to manipulate the actual uitextview.
First tried making a button overlay that could be moved and could be pressed to start editing, but it wasn't centered properly.
Then tried the above method to move the UITextView itself. But it would only work on touches or drags. (Note this was a modified form of touchesBegan & touchesMoved)
Ended up with a UIScrollView with the UITextView as a subview. Now it can move smoothly just that it can be moved from any place on the screen. Not optimal but is the best result to thus keep everything else intact.
Does the textView need to support scrolling? If so, this could get complicated.
But if not, there are two approaches. 1) subclass the textview and override touchesBegan, touchesMoved, touchesEnded. 2) write a gesture recognizer that processes the same messages and attach it to the textview.
Here's an example of a Gesture recognizer that will do the job:
#interface TouchMoveGestureRecognizer : UIGestureRecognizer
{
CGPoint _ptOffset;
}
#end
#implementation TouchMoveGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
_ptOffset = [t locationInView: self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
CGPoint pt = [t locationInView: self.view.superview];
pt.x -= _ptOffset.x;
pt.y -= _ptOffset.y;
CGRect r = self.view.frame;
r.origin = pt;
self.view.frame = r;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_ptOffset = CGPointMake(-1, -1);
}
#end
and, how to use it:
- (void)viewDidLoad {
[super viewDidLoad];
_textView.scrollEnabled = NO;
TouchMoveGestureRecognizer* gr = [[[TouchMoveGestureRecognizer alloc] init] autorelease];
[_textView addGestureRecognizer: gr];
}
In touchesBegan:
CGPoint touch_point = [[touches anyObject] locationInView:self.view];
There are tens of UIImageView around, stored in a NSMutableArray images. I'd like to know is there a built-in function to check if a CGPoint (touch_point) is inside one of the images, e.g.:
for (UIImageView *image in images) {
// how to test if touch_point is tapped on a image?
}
Thanks
Follow up:
For unknown reason, pointInside never returns true. Here is the full code.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
touch_point = [touch locationInView:self.view];
for (UIImageView *image in piece_images) {
if ([image pointInside:touch_point withEvent:event]) {
image.hidden = YES;
} else {
image.hidden = NO;
}
NSLog(#"image %.0f %.0f touch %.0f %.0f", image.center.x, image.center.y, touch_point.x, touch_point.y);
}
}
although I can see the two points are sometimes identical in the NSLog output.
I also tried:
if ([image pointInside:touch_point withEvent:nil])
the result is the same. never returns a true.
To eliminate the chance of anything goes with with the images. I tried the following:
if (YES or [image pointInside:touch_point withEvent:event])
all images are hidden after the first click on screen.
EDIT 2:
Really weird. Even I hard coded this:
point.x = image.center.x;
point.y = image.center.y;
the code becomes:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point; // = [touch locationInView:self.view];
for (UIImageView *image in piece_images) {
point.x = image.center.x;
point.y = image.center.y;
if ([image pointInside:point withEvent:event]) {
image.hidden = YES;
NSLog(#"YES");
} else {
image.hidden = NO;
NSLog(#"NO");
}
NSLog(#"image %.0f %.0f touch %.0f %.0f", image.center.x, image.center.y, point.x, point.y);
}
}
pointInside always returns false ...
Sounds to me like the problem is you need to convert coordinates from your view's coordinates to the UIImageView's coordinates. You can use UIView's convertPoint method.
Try replacing
if ([image pointInside:touch_point withEvent:event]) {
with
if ([image pointInside: [self.view convertPoint:touch_point toView: image] withEvent:event]) {
(Note that you are allowed to pass nil instead of event.)
I guess this is a bit late for the questioner, but I hope it is of use to someone.
if ([image pointInside:touch_point withEvent:event]) {
// Point inside
}else {
// Point isn't inside
}
In this case event is taken from :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
for (UIImageView *piece in piece_images) {
CGFloat x_min = piece.center.x - (piece.bounds.size.width / 2);
CGFloat x_max = x_min + piece.bounds.size.width;
CGFloat y_min = piece.center.y - (piece.bounds.size.height / 2);
CGFloat y_max = y_min + piece.bounds.size.height;
if (point.x > x_min && point.x < x_max && point.y > y_min && point.y < y_max ) {
piece.hidden = YES;
} else {
piece.hidden = NO;
}
}
}
too bad, I have do it myself...
it's OS 3.2, XCode 3.2.2, tried both on simulator and iPad
I am doing multi touch on UImageView means zoom in and zoom out on image view. I am using followng code but it doesn't work very well. Can anyone look at this code,
#import "ZoomingImageView.h"
#implementation ZoomingImageView
#synthesize zoomed;
#synthesize moved;
define HORIZ_SWIPE_DRAG_MIN 24
define VERT_SWIPE_DRAG_MAX 24
define TAP_MIN_DRAG 10
CGPoint startTouchPosition;
CGFloat initialDistance;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
moved = NO;
zoomed = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void)dealloc {
if([timer isValid])
[timer invalidate];
[super dealloc];
}
- (void) setImage: (UIImage*)img
{
zoomed = NO;
moved = NO;
self.transform = CGAffineTransformIdentity;
[super setImage:img];
}
- (CGFloat)distanceBetweenTwoPoints:(CGPoint)fromPoint toPoint:(CGPoint)toPoint {
float x = toPoint.x - fromPoint.x;
float y = toPoint.y - fromPoint.y;
return sqrt(x * x + y * y);
}
- (CGFloat)scaleAmount: (CGFloat)delta {
CGFloat pix = sqrt(self.frame.size.width * self.frame.size.height);
CGFloat scale = 1.0 + (delta / pix);
return scale;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if([timer isValid])
[timer invalidate];
moved = NO;
switch ([touches count]) {
case 1:
{
// single touch
UITouch * touch = [touches anyObject];
startTouchPosition = [touch locationInView:self];
initialDistance = -1;
break;
}
default:
{
// multi touch
UITouch *touch1 = [[touches allObjects] objectAtIndex:0];
UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
initialDistance = [self distanceBetweenTwoPoints:[touch1 locationInView:self]
toPoint:[touch2 locationInView:self]];
break;
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch1 = [[touches allObjects] objectAtIndex:0];
if([timer isValid])
[timer invalidate];
/*if ([touches count] == 1) {
CGPoint pos = [touch1 locationInView:self];
self.transform = CGAffineTransformTranslate(self.transform, pos.x - startTouchPosition.x, pos.y - startTouchPosition.y);
moved = YES;
return;
}****/
if ((initialDistance > 0) && ([touches count] > 1)) {
UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
CGFloat currentDistance = [self distanceBetweenTwoPoints:[touch1 locationInView:self]
toPoint:[touch2 locationInView:self]];
CGFloat movement = currentDistance - initialDistance;
NSLog(#"Touch moved: %f", movement);
CGFloat scale = [self scaleAmount: movement];
self.transform = CGAffineTransformScale(self.transform, scale, scale);
// }
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch1 = [[touches allObjects] objectAtIndex:0];
if ([touches count] == 1) {
// double tap to reset to default size
if ([touch1 tapCount] > 1) {
if (zoomed) {
self.transform = CGAffineTransformIdentity;
moved = NO;
zoomed = NO;
}
return;
}
}
else {
// multi-touch
UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
CGFloat finalDistance = [self distanceBetweenTwoPoints:[touch1 locationInView:self]
toPoint:[touch2 locationInView:self]];
CGFloat movement = finalDistance - initialDistance;
NSLog(#"Final Distance: %f, movement=%f",finalDistance,movement);
if (movement != 0) {
CGFloat scale = [self scaleAmount: movement];
self.transform = CGAffineTransformScale(self.transform, scale, scale);
NSLog(#"Scaling: %f", scale);
zoomed = YES;
}
}
}
- (void)singleTap: (NSTimer*)theTimer {
// must override
}
- (void)animateSwipe: (int) direction {
// must override
}
It is not working fine on device. can anyone tell that where i am wrong.
When you use any CGAffinTransform.... the value of the frame property becomes undefined. In your code you are using the frame.size.width and frame.size.height to calculate the change in size. After the first iteration of CGAffinTransformScale you would not get the right scale factor. According to the documentation bounds would be the right property for scale calculations.