I have a uiimage that has a pan gesture enabling the user to move the image along the y axis. How can I make it impossible for the image position from becoming <100 for example.
Basically, I have a draggable image that I don't want to be dragged beyond a certain point on the y axis.
I had a long search for something like this but found nothing that I thought was relevent enough to me.
I am very new to programming and would appreciate any help.
Thanks in advance.
If you can drag the image, you have surely a gesture recognizer that calls a callback method during dragging repeatedly. And you probably use the current coordinate provided by the gesture recognizer to update the center property of the image that you drag.
So you could check the current coordinate in the callback method, and if it crossed a certain border, you simply do no longer update the center property of the image.
Or did I understand something wrong?
Maybe you could provide some code. This would make an answer easier.
In the pan handling method you can use something like this:
- (void)panHandle:(UIPanGestureRecognizer*)gesture
{
CGPoint point = [gesture locationInView:self.view];//get the coordinate
//check for current position of image and update only
//if it is below 100 or a desired value
if(point.y <= 100)
{
//update the coordinate of image
}
else
{
point.y = 100;
//update the coordinate. This will make your view move on x axis keeping y limits
}
}
Related
I've got a UIView laying right above all others. This UIView has a UIPanGestureRecognizer to drag it around of the screen. If the User stopped panning, the View moves to a calculated Position which is the left screen bounds or the right ones. This works quite good but now I want to consider the velocity the View moved around the screen and when the user lifts his Finger off the screen, the UIView should not stop immediately to move to the endpoint, instead it should do it with a curve.
Edit 1
I think I expressed myself wrongly. I meant that the View should move the direction the user dragged it to but then it should make a curve and move to the desired position. Think of it like gravity. The user will drag the View with a speed from the left bottom corner to the middle of the screen and lifts immediately his finger. Then the View should not stop, else it should move in the last direction und slowly move to the desired location, which is in this case the right bottom corner.
This is the code I already have:
private void DidPan()
{
CGPoint translation = PanGesture.TranslationInView(this.Superview);
CGPoint velocity = PanGesture.VelocityInView(this.Superview);
if (PanGesture.State == UIGestureRecognizerState.Changed)
{
this.Center = new CGPoint(lastLocation.X + translation.X, lastLocation.Y + translation.Y);
}
else if (PanGesture.State == UIGestureRecognizerState.Ended)
{
// Calculates the new position the view will be moving to
var newLocation = new CGPoint(SetX(Superview.Bounds), SetY(Superview.Bounds));
// Animate it to the desired location
UIView.Animate(0.3, () =>
{
this.Center = newLocation;
}, null);
}
}
As you can see we already have the velocity but I don't know how to continue from here. Hopefully someone has the right starting point from here and could help me.
Thanks a lot!
I think UIKit Dynamics can meet your requirement.
refer to this Blog : https://blog.xamarin.com/drag-drop-and-snap-with-uikit-dynamics/
I'm using UIKit Dynamics to push a UIView off screen, similar to how Tweetbot performs it in their image overlay.
I use a UIPanGestureRecognizer, and when they end the gesture, if they exceed the velocity threshold it goes offscreen.
[self.animator removeBehavior:self.panAttachmentBehavior];
CGPoint velocity = [panGestureRecognizer velocityInView:self.view];
if (fabs(velocity.y) > 100) {
self.pushBehavior = [[UIPushBehavior alloc] initWithItems:#[self.scrollView] mode:UIPushBehaviorModeInstantaneous];
[self.pushBehavior setTargetOffsetFromCenter:centerOffset forItem:self.scrollView];
self.pushBehavior.active = YES;
self.pushBehavior.action = ^{
CGPoint lowestPoint = CGPointMake(CGRectGetMinX(self.imageView.bounds), CGRectGetMaxY(self.imageView.bounds));
CGPoint convertedPoint = [self.imageView convertPoint:lowestPoint toView:self.view];
if (!CGRectIntersectsRect(self.view.bounds, self.imageView.frame)) {
NSLog(#"outside");
}
};
CGFloat area = CGRectGetWidth(self.scrollView.bounds) * CGRectGetHeight(self.scrollView.bounds);
CGFloat UIKitNewtonScaling = 5000000.0;
CGFloat scaling = area / UIKitNewtonScaling;
CGVector pushDirection = CGVectorMake(velocity.x * scaling, velocity.y * scaling);
self.pushBehavior.pushDirection = pushDirection;
[self.animator addBehavior:self.pushBehavior];
}
I'm having an immense amount of trouble detecting when my view actually completely disappears from the screen.
My view is setup rather simply. It's a UIScrollView with a UIImageView within it. Both are just within a UIViewController. I move the UIScrollView with the pan gesture, but want to detect when the image view is off screen.
In the action block I can monitor the view as it moves, and I've tried two methods:
1. Each time the action block is called, find the lowest point in y for the image view. Convert that to the view controller's reference point, and I was just trying to see when the y value of the converted point was less than 0 (negative) for when I "threw" the view upward. (This means the lowest point in the view has crossed into negative y values for the view controller's reference point, which is above the visible area of the view controller.)
This worked okay, except the x value I gave to lowestPoint really messes everything up. If I choose the minimum X, that is the furthest to the left, it will only tell me when the bottom left corner of the UIView has gone off screen. Often times as the view can be rotating depending on where the user pushes from, the bottom right may go off screen after the left, making it detect it too early. If I choose the middle X, it will only tell me when the middle bottom has gone off, etc. I can't seem to figure out how to tell it "just get me the absolute lowest y value.
2. I tried CGRectIntersectsRect as shown in the code above, and it never says it's outside, even seconds after it went shooting outside of any visible area.
What am I doing wrong? How should I be detecting it no longer being visible?
If you take a look on UIDynamicItem protocol properties, you can see they are center, bounds and transform. So UIDynamicAnimator actually modifies only these three properties. I'm not really sure what happens with the frame during the Dynamics animations, but from my experience I can tell it's value inside the action block is not always reliable. Maybe it's because the frame is actually being calculated by CALayer based on center, transform and bounds, as described in this excellent blog post.
But you for sure can make use of center and bounds in the action block. The following code worked for me in a case similar to yours:
CGPoint parentCenter = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
self.pushBehavior.action = ^{
CGFloat dx = self.imageView.center.x - parentCenter.x;
CGFloat dy = self.imageView.center.y - parentCenter.y;
CGFloat distance = sqrtf(dx * dx + dy * dy);
if(distance > MIN(parentCenter.y + CGRectGetHeight(self.imageView.bounds), parentCenter.x + CGRectGetWidth(self.imageView.bounds))) {
NSLog(#"Off screen!");
}
};
I have a center point for an UIImageView which is being updated.
At present the issue is that I see the image view move. What I am looking for is to move the imageview when the view is hidden and then show it again once the center point is updated.
I created a method that does the center point update:
-(void)updateCenterPoint {
float newhintX = (self.originalImageCenterPoint.x * self.scaleDownOriginalImageRatio) + self.insideImageFrame.frame.origin.x;
float newhintY = (self.originalImageCenterPoint.y * self.scaleDownOriginalImageRatio) + self.insideImageFrame.frame.origin.y;
if (self.hintView.hidden == NO) {
self.hintView.hidden = YES;
self.hintView.center = CGPointMake(newhintX, newhintY);
} else {
self.hintView.center = CGPointMake(newhintX, newhintY);
}
if (self.hintView.hidden) {
self.hintView.hidden = NO;
}
}
Calling this in viewDidLoad does not appear to work how I expect it to. Ideally when the view loads I do not want to the see the hintView (image view) until it is in the correct point?
Why not just do something like this:
Hide the image
Move the image
Unhide the image
Instead of asking if it is hidden etc. explicitly hide it, move it, then unhide it.
Jeff
The issue was that I was updating the center point from a method:
-(void)viewDidLayoutSubviews
This is called at some point which is too early. So the center point is still 0,0 even though it is being updated.
This meant that the view was becoming unhidden/shown before the center point had changed, therefore when the method was called again the update occurred to the centerpoint.
The resolution was to check that the point was not 0,0 if it was keep it hidden. It is never going to be 0,0 in my example - luckily.
I need to crop an image to match a specific dimension. I have three layers in my view starting from the bottom:
I have the raw image in a UIImage. This image comes from the camera. (called cameraImage)
I have a UIView holding this image. When user clicks "crop", the UIView's bounds are used to crop the raw image inside it.
Above all of this I have a guide image which shows the user the dimensions they need to pan, rotate, and pinch their image to fit into.
I want to add the pan gesture to the top guide image and have it control the raw image at the bottom. So the guide image never moves but it is listening for the pan gesture.
I can't figure out how to reset the recognizer without it making my raw image jump back to zero. Maybe someone could help?
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:recognizer.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x+translation.x, recognizer.view.center.y+ translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:recognizer.view];
}
The above code works great in my gesture is attached to the bottom image. The problem is when the user goes outside the view's bounds, the image stops panning and is basically stuck. You can't touch it anymore so it sits there. So I thought if i attached the gesture to the top it would solve this problem.
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:recognizer.view];
cameraImage.center = CGPointMake(recognizer.view.center.x+translation.x, recognizer.view.center.y+ translation.y);
}
This almost works. I set the cameraImage's center and removed the third line which resets the recognizer. If I don't remove it, the cameraImage jumps back into the same position every time I try and pan. It almost works because when you click the image again it doesn't start from the pixel you touched. It moves the image back to the original position and then lets you pan.
First option:
when the recognizer enter the UIGestureRecognizerStateEndedstate
if(recofnizer.state == UIGestureRecognizerStateEnded )
{
...
}
You store the translation at that point in time in an instance varibale (#property) of your class.
And then you always add the saved translation to the new translation.
In code this would look like this:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint updatedTranslation = CGPointMake(translation.x+self.savedTranslation.x,translation.y+self.savedTranslation.y);
cameraImage.center = CGPointMake(recognizer.view.center.x+updatedTranslation.x, recognizer.view.center.y+ updatedTranslation.y);
if(recofnizer.state == UIGestureRecognizerStateEnded )
{
self.savedTranslation = updatedTranslation;
}
}
Dont forget to add #property (nonatomic, assign) CGPoint savedTranslation; to your interface.
Also make sure the savedTranslation variable is initialized in the init method of your class to self.savedTranslation = CGPointMake(0,0);
Second option:
You should think about doing all that what you want in an scrollview with the imageview as the viewForZooming of the scrollview. This allows very smooth interaction, like users are used to!
Above this scrollview you can then put your mask/guide, but make sure to disable the userInteraction of the mask/guide view to make your user touch the scrollview below!
Basically I would just like to touch the image and have it move with my finger across the screen without using theImage.center = touchPosition
I don't want to have the center snap to my finger. I want to move the image from whatever point I touch.
The standard way to handle this, which #richard-j-ross-iii hinted at in a comment, is to save the difference between the view's center and the touch position when the drag starts and then maintain that difference as the drag proceeds. In your touchesBegan:withEvent: method save the offset like:
_dragOffsetFromCenter = CGSizeMake(touchLocationInImageView.x - centerOfImageView.x, touchLocationInImageView.y - centerOfImageView.y);
then in touchesMoved:withEvent: you can maintain that same offset like:
myImageView.center = CGPointMake(touchLocation.x - _dragOffsetFromCenter.x, touchLocation.y - _dragOffsetFromCenter.y);