Moving a UIImageView without using the center property - ios

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);

Related

Gesture-driven animation between frames

I am trying to create an interactive animation between a thumbnail frame and a fullscreen frame. Panning upwards could cause the frame to go grow in both dimensions (until reaching fullscreen) while panning downwards does the opposite. Very similar to how Youtube animates their video player.
I tried using a UIPanGestureRecognizer, and a POPSpringAnimation that activates when the recognizers state is UIGestureRecognizerStateEnded, like so:
- (void)didPan:(UIPanGestureRecognizer *)recognizer
{
CGPoint point = [recognizer translationInView:self.view.superview];
self.view.center = CGPointMake(self.view.center.x, self.view.center.y + point.y);
[recognizer setTranslation:CGPointZero inView:self.view.superview];
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint velocity = [recognizer velocityInView:self.view.superview];
// Initaite POPSpringAnimation using velocity and target frame
// either fullscreen or thumbnail
}
}
The effect of this is that the view's center gets updated while panning, but its size will only start changing after you stop panning. I do not want to manually resize the frame in didPan: without knowing at what ratio to do it.
How can I create a panning-driven animation that simply goes from frame A to frame B?
This framework does almost the same thing, but the animation is not interactive. Same thing goes for this answer.
I suggest using a UIViewPropertyAnimator, a class addd in iOS 10. It lets you pretty easily pause, reverse, or scrub animations back and forth.
I have a demo project on GitHub (Written in Swift, which might be harder for you to follow, since you're using Objective-C:
https://github.com/DuncanMC/UIViewPropertyAnimator-test
The class is actually pretty easy to use. You should be able to figure it out from the docs, and the sample project could at least serve as a roadmap on the APIs to use even if you can't follow the code line-by-line

Changing UIScrollView pinch zoom speed

I would like to decrease the zooming speed behavior of a UIScrollView.
I've tried the solutions given in this two answers: Answer 1, Answer 2, but I'm not getting the expected results.
This is my implementation of answer 2:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
if(scrollView.multipleTouchEnabled){
CGFloat fSpeedZoom = 2;
CGFloat fMaxZoomScale = scrollView.maximumZoomScale;
CGFloat fMinZoomScale = scrollView.minimunZoomScale;
scrollView.maximumZoomScale = scrollView.zoomScale + fSpeedZoom;
scrollView.minimumZoomScale = scrollView.zoomScale - fSpeedZoom;
if(scrollView.maximumZoomScale > fMaxZoomScale){
self.scrollView.maximumZoomScale = fMaxZoomScale;
}
if(scrollView.minimumZoomScale < fMinZoomScale){
self.scrollView.minimumZoomScale = fMinZoomScale;
}
}
}
If I keep the fSppedZoom values between 2 and 5 I get kind of an exponential behavior in which the further I try to zoom, the slower it zooms. I would like to get a linear behavior in which with a single parameter I could control the zooming speed.
I would recommend against doing this.
When you drag, the point that was underneath your finger when the drag started stays underneath your finger as the drag pans. This remains the case until you reach the edge of the draggable area, at which time the point moves in the same direction as the pan, but not as far, to simulate resistance.
When you pinch to zoom, the same thing happens. The two points that were underneath your two fingers stay underneath your finger as the pinch moves (hence you can pan at the same time as zooming). Again, this only breaks down when further zooming is not possible.
If you break this touch-point relationship, user interactions feel alien and unnatural.
If you absolutely must do this, I would recommend, instead of trying to fight UIScrollView's built-in zooming behaviour, implementing your own pinch gesture recogniser and considering the relationship you want to achieve between the positions of your touches and the area of the scroll view you want to show.

Is it possible to perform a dissolve animation but control the progress, such as how you can with alpha?

I want to dissolve one UIImageView into another, but control the progress (so it occurs as the user pans their finger). This is simple with alpha, as I just change the UIView's alpha value as the user pans, but I don't know how to do it for a dissolve animation, which is slightly different from alpha changing.
Dissolve animation between two images can be made by placing one image on top of other and reducing alpha of the topmost image. This can be done in animation block.
In case of changing dissolve according to user's pan gesture can be written method like:
- (void)setDissolveState:(CGFloat)state
{
self.imageTop.alpha = 1.0 - state;
// ensure bottom image is opaque, this can be done once
self.imageBottom.alpha = 1.0;
}
Here imageTop and imageBottom are UIImage's properties of the object (for example of the view controller) who processes pan events. When change in pan is discovered the method setDissolveState: should be called with relative position of the touch:
CGFloat relativePosition = (touchLocation.x - self.view.frame.origin.x) / self.view.frame.size.width;
[self setDissolveState:relativePosition];

Prevent draggable uiimage being moved beyond point on y axis

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
}
}

Panning a UIImage with a Gesture Recognizer on a placeholder UIView

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!

Resources