I have a strange problem with using UIPanGestureRecognizer to move a view from the top of the screen. Everything works perfect in simulator but on the actual device the view dissappears when I touch it.
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint start;
if (recognizer.state == UIGestureRecognizerStateBegan) {
start = [recognizer translationInView:self.view];
originCenter = recognizer.view.center.y;
}
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:self.view];
if (down) {
if (originCenter + translation.y < self.view.center.y && self.view.center.y + translation.y < self.view.center.y) {
CGPoint move = CGPointMake(self.view.frame.size.width/2, originCenter-(start.y-translation.y));
[recognizer.view setCenter:move];
}
}else{
if (originCenter + translation.y < self.view.center.y && originCenter + translation.y > originCenter) {
CGPoint move = CGPointMake(self.view.frame.size.width/2, originCenter-(start.y-translation.y));
[recognizer.view setCenter:move];
}
}
}
if([recognizer state] == UIGestureRecognizerStateEnded || [recognizer state] == UIGestureRecognizerStateCancelled ){
if(recognizer.view.center.y > -40){
[recognizer.view setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
down = TRUE;
}else{
[recognizer.view setFrame:CGRectMake(0, 80-self.view.frame.size.height, self.view.frame.size.width, self.view.frame.size.height)];
down = FALSE;
}
}
}
There is one thing that comes to my mind that there might be problem with calculating the position on the device and the view is moved outside the screen. Is that possible?
If nothing else, you should make start a static or an ivar. Otherwise, when you hit UIGestureRecognizerStateChanged, start will not have preserved the value it had during UIGestureRecognizerStateBegan.
Related
I am trying to slide the view in both directions,and following code is working fine for entire view, but i need this pan gesture works only for left and right corners only say 50 pixels on each side, and neglect the gesture if its not on the corners.
please help
-(void)setupGestures {
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(movePanel:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[_centerViewController.view addGestureRecognizer:panRecognizer];
}
-(void)movePanel:(id)sender {
[[[(UITapGestureRecognizer*)sender view] layer] removeAllAnimations];
CGPoint translatedPoint = [(UIPanGestureRecognizer*)sender translationInView:self.view];
CGPoint velocity = [(UIPanGestureRecognizer*)sender velocityInView:[sender view]];
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
UIView *childView = nil;
if(velocity.x > 0) {
if (!_showingRightPanel) {
childView = [self getLeftView];
}
} else {
if (!_showingLeftPanel) {
childView = [self getRightView];
}
}
// make sure the view we're working with is front and center
[self.view sendSubviewToBack:childView];
[[sender view] bringSubviewToFront:[(UIPanGestureRecognizer*)sender view]];
}
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
if(velocity.x > 0) {
// NSLog(#"gesture went right");
} else {
// NSLog(#"gesture went left");
}
if (!_showPanel) {
[self movePanelToOriginalPosition];
} else {
if (_showingLeftPanel) {
[self movePanelRight];
} else if (_showingRightPanel) {
[self movePanelLeft];
}
}
}
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateChanged) {
if(velocity.x > 0) {
// NSLog(#"gesture went right");
} else {
// NSLog(#"gesture went left");
}
// are we more than halfway, if so, show the panel when done dragging by setting this value to YES (1)
_showPanel = abs([sender view].center.x - _centerViewController.view.frame.size.width/2) > _centerViewController.view.frame.size.width/2;
// allow dragging only in x coordinates by only updating the x coordinate with translation position
[sender view].center = CGPointMake([sender view].center.x + translatedPoint.x, [sender view].center.y);
[(UIPanGestureRecognizer*)sender setTranslation:CGPointMake(0,0) inView:self.view];
// if you needed to check for a change in direction, you could use this code to do so
if(velocity.x*_preVelocity.x + velocity.y*_preVelocity.y > 0) {
// NSLog(#"same direction");
} else {
// NSLog(#"opposite direction");
}
_preVelocity = velocity;
}
}
Get the four corners of the UIView using yourView.frame
Then, get the point of touch inside the UIView.
See the difference between the point of touch and each of the four corners.
If any one of the four values is < 50 (say), do what you want in the method which is called through the UIPanGestureRecognizer.
You can the point of touch through
CGPoint currentlocation = [recognizer locationInView:self.view];
You can get the four corners through,
CGPoint topLeft = view.bounds.origin;
topLeft = [[view superview] convertPoint:topLeft fromView:view];
CGPoint topRight = CGPointMake(view.bounds.origin.x + view.bounds.width, view.bounds.origin.y);
topRight = [[view superview] convertPoint:topRight fromView:view];
// and so on.
You can find the distance between two points through this method,
- (double) getDistance:(CGPoint)one :(CGPoint)two
{
return sqrt((two.x - one.x)*(two.x - one.x) + (two.y - one.y)*(two.y - one.y));
}
Hope this helps (-:
Override the method - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event in the UIView to look like the following
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL pointInside = NO;
if(CGRectContainsPoint(self.bounds, point) && ((point.x < 50) || (point.x > self.bounds.size.width-50)))
{
pointInside = YES;
}
return pointInside;
}
Hope it helps.
I'm trying to implement something similar to how Mac/iOS web pages are implemented, where if you pull past a certain threshold, the main view becomes "stretchy" and moves based on the velocity of the pull.
The difference, though, is I'm trying to do this horizontally for UITableViewCells with a UIPanGestureRecognizer.
I've got a handlePan method:
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
[recognizer setMaximumNumberOfTouches:1];
CGPoint velocity = [recognizer velocityInView:self];
int panDelta = ([recognizer locationInView:self].x - [recognizer locationInView:self.superview].x);
if (recognizer.state == UIGestureRecognizerStateBegan) {
_originalCenter = self.center;
}
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:self];
float xVal = 0;
if (panDelta <= 38) {
xVal = _originalCenter.x + translation.x;
} /*else {
// this is where I struggle.
xVal = _originalCenter.x;
}*/
self.center = CGPointMake(xVal, _originalCenter.y);
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGRect newFrame;
int xOffset = 0;
newFrame = CGRectMake(xOffset, self.frame.origin.y,
self.bounds.size.width, self.bounds.size.height);
[UIView animateWithDuration:0.2 animations:^{
self.frame = newFrame;
}];
}
}
I don't have any specific algorithm to create the "stretchiness"; all I'm trying to do is make it so the sliding doesn't go as wide as the user's interaction is, and is fluid.
Ideas?
I think you are looking for a simple function with a horizontal asymptote indicating a maximum offset:
// this is where I struggle.
CGFloat maxOffset = 50.0; // change as you like
CGFloat elementWidth = ?; // fill in the width of your view animated, eg: self.frame.size.width
CGFloat absPannedOffset = fabsf(translation.x);
CGFloat rico = powf(elementWidth, 2) / maxOffset;
CGFloat absOffset = (((- 1 / rico) * powf(absPannedOffset - elementWidth, 2)) + maxOffset);
if (pannedOffset < 0) {
xVal = -absOffset;
} else {
xVal = absOffset;
}
I am having my UIImageView onto which I am having another UIView rectangle. By applying pan gesture to UIView rectangle it gets outside of UIImageView also. I don't want to be drag outside of UIImageView
I have tried below code but it is not working that way
-(void)handleMovementView:(UIPanGestureRecognizer *)recognizer
{
CGPoint movement;
if(recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged || recognizer.state == UIGestureRecognizerStateEnded)
{
CGRect rec = recognizer.view.frame;
CGRect imgvw = self.imgViewCrop.frame;
if((rec.origin.x >= imgvw.origin.x && (rec.origin.x + rec.size.width <= imgvw.origin.x + imgvw.size.width)))
{
CGPoint translation = [recognizer translationInView:recognizer.view.superview];
movement = translation;
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointZero inView:recognizer.view.superview];
[self handleMovementForHandlers:movement];
}
}
}
If i apply Pan slowly it applies this condition but when i go fast it went outside of ImageView
Instead of manually computing whether the points are within the view's bounds, use CGRectContainsPoint(rect, point). This is what works for me, and I like it because it's shorter and more readable:
func handlePan(pan: UIPanGestureRecognizer) {
switch pan.state {
case .Began:
if CGRectContainsPoint(self.pannableView.frame, pan.locationInView(self.pannableView)) {
// Gesture started inside the pannable view. Do your thing.
}
}
Try This
-(void)handleMovementView:(UIPanGestureRecognizer *)recognizer
{
CGPoint movement;
if(recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged || recognizer.state == UIGestureRecognizerStateEnded)
{
CGRect rec = recognizer.view.frame;
CGRect imgvw = self.imgViewCrop.frame;
if((rec.origin.x >= imgvw.origin.x && (rec.origin.x + rec.size.width <= imgvw.origin.x + imgvw.size.width)))
{
CGPoint translation = [recognizer translationInView:recognizer.view.superview];
movement = translation;
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
rec = recognizer.view.frame;
if( rec.origin.x < imgvw.origin.x )
rec.origin.x = imgvw.origin.x;
if( rec.origin.x + rec.size.width > imgvw.origin.x + imgvw.size.width )
rec.origin.x = imgvw.origin.x + imgvw.size.width - rec.size.width;
recognizer.view.frame = rec;
[recognizer setTranslation:CGPointZero inView:recognizer.view.superview];
[self handleMovementForHandlers:movement];
}
}
}
Expanding on #Matt Quiros' answer, and in Swift 3 / 4:
func shouldRespondToGesture(_ gesture: UIGestureRecognizer, in frame: CGRect) -> Bool {
return gesture.state == .began && frame.contains(gesture.location(in: self.view))
}
First get the coordinate (CGRect) of recognizer(Pan Gesture recognizer) then get coordinate (CGRect) of ImageView(Your ImageView).
And then compare this coordinate according to your requirement.
If you are still facing any problem then log the x and y value of image view and recognizer.
I am using the following code to rotate an image by starting gesture on left and right arrows.While moving arrows along the rotation.
- (void)handleRotation:(UIPanGestureRecognizer *)recognizer
{
UIView* cview = self.superview.superview;
UIView* lView = self.superview;
CGPoint origin = [lView convertPoint: self.center toView: cview];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
if (recognizer.view == leftImage)
{
initialP= [self convertPoint: leftImage.center toView: cview];
}
else
{
initialP = [self convertPoint: rightImage.center toView: cview];
}
}
CGPoint currentP = [recognizer translationInView:cview];
currentP.x += initialP.x;
currentP.y += initialP.y;
CGFloat angle = 0;
CGFloat a = initialP.x - origin.x;
CGFloat b = initialP.y - origin.y;
CGFloat c = currentP.x - origin.x;
CGFloat d = currentP.y - origin.y;
angle = acosf( ((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
angle += lastReleasedAngle;
if (currentP.y < (currentP.x *(initialP.y - origin.y) + (initialP.x * origin.y - initialP.y * origin.x)) / (initialP.x - origin.x))
{
angle = -angle;
}
if (recognizer.view == leftImage)
{
angle = -angle;
}
if(recognizer.state == UIGestureRecognizerStateBegan ||
recognizer.state == UIGestureRecognizerStateChanged || recognizer.state == UIGestureRecognizerStateEnded)
{
[self setTransform: CGAffineTransformMakeRotation(angle)];
}
if (recognizer.state == UIGestureRecognizerStateEnded)
{
lastReleasedAngle = angle;
}
First time when starts rotating it works perfectly fine.Say,right arrow is at 0 degree now..when i start rotation from here and release here it works fine.When i release the rotation anywhere else then tries to rotate ,image starts flipping exact to 180 degree after moving 180 degree.
check out the example over here of rotation with left ,right and depend on position wise download from here
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:self];
[templatePhotoPlaceholderView addGestureRecognizer:panRecognizer];
-(void)move:(UIPanGestureRecognizer *)gestureRecognizer{
CGPoint translatedPoint = [gestureRecognizer translationInView:imageview];
CGPoint center;
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
_firstX = [imageview center].x;
_firstY = [imageview center].y;
if(center.x < templatePhotoPlaceholderView.frame.origin.x + (imageview.frame.size.width/2)){
center.x = templatePhotoPlaceholderView.frame.origin.x+(imageview.frame.size.width/2);
}
if(center.x > templatePhotoPlaceholderView.frame.origin.x+templatePhotoPlaceholderView.frame.size.width - (imageview.frame.size.width/2)){
center.x = templatePhotoPlaceholderView.frame.origin.x+templatePhotoPlaceholderView.frame.size.width - (imageview.frame.size.width/2);
}
if(center.y < templatePhotoPlaceholderView.frame.origin.y + (imageview.frame.size.height/2)){
center.y = templatePhotoPlaceholderView.frame.origin.y + (imageview.frame.size.height/2);
}
if(center.y > templatePhotoPlaceholderView.frame.origin.y + templatePhotoPlaceholderView.frame.size.height -(imageview.frame.size.height/2)){
center.y = templatePhotoPlaceholderView.frame.origin.y + (templatePhotoPlaceholderView.frame.size.height)-(imageview.frame.size.height/2);
}
}
translatedPoint = CGPointMake(center.x+translatedPoint.x, center.y+translatedPoint.y);
[imageview setCenter:translatedPoint];
}
how to move an image in a specified view [templatePhotoPlaceholderView is a view imageview is an UIImageView]
When the image is touches the edges of all corner of UIView then image need not to move.
Not to allow the image to go out side of the UIView.
i try but not able to fix the image view in specified region to be moved.
# sorry if any grammatical mistake in typing.
#all please advice me how to figure the issue.
// Scaling
UIPinchGestureRecognizer *pinchRecognizer = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scale:)] autorelease];
[pinchRecognizer setDelegate:self];
[templatePhotoPlaceholderView addGestureRecognizer:pinchRecognizer];
[self.view addSubview:templatePhotoPlaceholderView];
[tapRecognizer release];
- (void)scale:(UIPinchGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
float hfactor = imageview.frame.size.width / templatePhotoPlaceholderView.frame.size.width;
float vfactor = imageview.frame.size.height / templatePhotoPlaceholderView.frame.size.height;
float factor = MAX(hfactor, vfactor);
if (([gestureRecognizer scale] > 1 && factor < 3) || ([gestureRecognizer scale] <= 1 && factor >1) ) {
imageview.transform = CGAffineTransformScale([imageview transform], [gestureRecognizer scale], [gestureRecognizer scale]);
}
[gestureRecognizer setScale:1];
}
}
If you are not scaling the image or view then it's very simple.
try this way:
-(void)move:(UIPanGestureRecognizer *)gestureRecognizer{
CGPoint translatedPoint = [gestureRecognizer translationInView:imageview];
CGPoint center;
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
_firstX = [imageview center].x;
_firstY = [imageview center].y;
//checking for x axis
if((self.view.frame.origin.x + 2 > _firstX) && (imageVIew.frame.size.width+_fisrtX < 340))
//perform move condition
//checking for Y axis
if(self.view.frame.origin.y+ 2 > _firstY) && (imageview.Frame.size.height + _firstY < 420)
//perform move condtion
}