Image is stretching while Pinch - ios

I'm getting images from remote server and displaying in UIImageView then doing pinch gesture to this imageview. But when i pinching image, i'm getting image stretching. It's loosing original resolution and quality.
mmageView=[[UIImageView alloc]initWithFrame:CGRectMake(50,50,150,150)];
[self.view addSubview:mmageView];
UIPinchGestureRecognizer *dbpinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(dbhandlePinch:)];
[mmageView addGestureRecognizer:dbpinchGesture];
UIPinchGesture:
-(void)dbhandlePinch:(UIPinchGestureRecognizer*)recognizer {
if([recognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
LastScale = [recognizer scale];
}
if ([recognizer state] == UIGestureRecognizerStateBegan ||
[recognizer state] == UIGestureRecognizerStateChanged) {
CGFloat currentScale = [[[recognizer view].layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
// const CGFloat kMaxScale = 2.0;
const CGFloat kMinScale = 0.8;
CGFloat newScale = 1 - (LastScale - [recognizer scale]);
// newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
CGAffineTransform transform = CGAffineTransformScale([[recognizer view] transform], newScale, newScale);
[recognizer view].transform = transform;
LastScale = [recognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}

For pinch zoom add your imageView in a scrollView and Import UIScrollViewDelegate
- (void)viewDidLoad
{
[super viewDidLoad];
//for pinch gesture
_scrollView.minimumZoomScale = 0.5;
_scrollView.maximumZoomScale = 6.0;
_scrollView.contentSize = CGSizeMake(_imageView.frame.size.width, _imageView.frame.size.height);
_scrollView.delegate = self;
}
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _imageView;
}

UIScrollView makes supporting the pinch gestures for zooming easy. Better solution in Apple Documentation.

Related

Set content size of scrollview relative to transform on zooming by UIPinchGestureRecognizer

I have scrollview and added UIPinchGestureRecognizer to scrollview. Now on gesture event I have zoom in or out scrollview and its handle action is as following :
static float lastScale = 1.0;
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer
{
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [gestureRecognizer scale];
}
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged)
{
CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 2.0;
const CGFloat kMinScale = 1.0;
CGFloat newScale = 1 - (lastScale - [gestureRecognizer scale]);
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
[gestureRecognizer view].transform = transform;
lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
I want to change contentSize in this method. Now, my question is that how to set scrollview content size so that after zoom out I can scroll to see all the content in scrollview.
Please help.
#interface HorizontalScrollView ()
{
UIView *container;
}
#end
#implementation HorizontalScrollView
#synthesize scrlView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
container = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.scrlView.frame.size.width, self.scrlView.frame.size.height)];
UIImage *image = [UIImage imageNamed:#"1.jpeg"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect rect = imageView.frame;
rect.size.height = 215;
rect.size.width = self.scrlView.frame.size.width;
rect.origin = CGPointMake(0, 0);
imageView.frame = rect;
[container addSubview:imageView];
[scrlView addSubview:container];
self.scrlView.minimumZoomScale=1.0;
self.scrlView.maximumZoomScale=6.0;
self.scrlView.delegate=self;
// set the content size so it can be scrollable
[self.scrlView setContentSize:CGSizeMake(1*self.scrlView.frame.size.width, 0)];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return container;
}

How to give a specific gesture recognizer

When I apply panDetected like this, the image is moving everywhere on the screen. I just want this feature to at the top in specific rectangle. What should I change?
-(void) panDetected: (UIPanGestureRecognizer * ) panRecognizer {
CGPoint translation = [panRecognizer translationInView: self.documentImageView];
CGPoint imageViewPosition = self.documentImageView.center;
imageViewPosition.x += translation.x;
imageViewPosition.y += translation.y;
self.documentImageView.center = imageViewPosition;
[panRecognizer setTranslation: CGPointZero inView: self.view];
}
And this one is just allowing me to zoom on the pic but only from middle point not every point on the pic.
-(void) handlePinchWithGestureRecognizer: (UIPinchGestureRecognizer * ) pinchGestureRecognizer {
CGFloat lastScale = 1.0;
if ([pinchGestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [pinchGestureRecognizer scale];
}
if ([pinchGestureRecognizer state] == UIGestureRecognizerStateBegan || [pinchGestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat currentScale = [
[
[pinchGestureRecognizer view].layer valueForKeyPath: #
"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 1.15;
const CGFloat kMinScale = 1.0;
CGFloat newScale = 1 - (lastScale - [pinchGestureRecognizer scale]);
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
self.documentImageView.transform = CGAffineTransformScale(self.documentImageView.transform, newScale, newScale);
lastScale = [pinchGestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
-(void) setExtractedImageForTableView: (UIImage * ) extractedImage {
self.documentImageView.image = nil;
self.documentImageView = nil;
self.documentImageView = [
[UIImageView alloc] initWithImage: extractedImage];
self.documentImageView.userInteractionEnabled = YES;
UIPinchGestureRecognizer * pinchGestureRecognizer = [
[UIPinchGestureRecognizer alloc] initWithTarget: self action: #selector(handlePinchWithGestureRecognizer: )];
[self.documentImageView addGestureRecognizer: pinchGestureRecognizer];
UIPanGestureRecognizer * panRecognizer = [
[UIPanGestureRecognizer alloc] initWithTarget: self action: #selector(panDetected: )];
[self.documentImageView addGestureRecognizer: panRecognizer];
self.documentImageView.contentMode = UIViewContentModeScaleAspectFit;
if (!CGSizeEqualToSize(extractedImage.size, CGSizeZero)) {
self.documentImageView.frame = CGRectMake(CGRectGetMinX(self.view.frame), CGRectGetMinY(self.tableView.frame), CGRectGetWidth(self.view.frame), (extractedImage.size.height / extractedImage.size.width) * CGRectGetWidth(self.view.frame));
self.tableView.tableHeaderView = self.documentImageView;
[self.tableView setContentOffset: CGPointZero animated: YES];
CGRect frame = self.documentImageView.frame;
frame.size.height = self.documentImageView.frame.size.height + 100;
self.tableView.frame = CGRectMake(0, 0, 200, 200);
self.tableView.tableHeaderView.frame = frame;
}
}
You can use UIGestureRecognizerDelegate to acheve this.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
The method above asks the delegate if a gesture recognizer should receive an object representing a touch. Therefore, you can check touch location and return NO when its not in the desired area.

How can I control imageView's moving by PanGestureRecognizer?

I want to move imageView by PanGestureRecognizer like this
(imageView can be scaled)
If imageView's position is (0,0), it can't move.
If imageView's position x is over 60, it can't move more.
If imageView's position y is over 80, it can't move more.
When imageView's scale is restored(1.0), it's position is (0,0).
It is difficult to restrict imageView's moving and position.
What should I do?
Here is my code.
img = [UIImage imageNamed:[NSString stringWithFormat:#"a.jpg"]];
imgView = [[UIImageView alloc]initWithImage:img];
imgView.frame = CGRectMake(0,0, self.view.frame.size.width, 448);
imgView.userInteractionEnabled = YES;
[self.view imgView];
- (void)panAction : (UIPanGestureRecognizer *)sender {
CGPoint CGP = imgView.center;
if(newScale != 1.0 && CGP.x-160 != 0 && CGP.y-224 != 50){
CGPoint p = [sender translationInView:self.view];
CGPoint movedPoint = CGPointMake(imgView.center.x + p.x, imgView.center.y + p.y);
imgView.center = movedPoint;
[sender setTranslation:CGPointZero inView:self.view];
}
}
-(void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [gestureRecognizer scale];
}
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 2.0;
const CGFloat kMinScale = 1.0;
newScale = 1 - (lastScale - [gestureRecognizer scale]);
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
[gestureRecognizer view].transform = transform;
lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
You must use the following condition check in your Pan Gesture action:
For you information, as you move the object, this panAction() method is called regularly at each pixel move.
- (void)panAction : (UIPanGestureRecognizer *)sender {
if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan)
{
//Do something whatever you need to be done while the pan gesture starts
}
if ([(UIPanGestureRecognizer *)sender state] == UIGestureRecognizerStateChanged)
{
//Do something whatever you need to be done while the imageView object is in the moving state
}
if ([(UIPanGestureRecognizer *)sender state] == UIGestureRecognizerStateEnded){
//Do something you need to do as you end the pan gesture.
}
}

How to reduce velocity of pinch-zoom UIGestureRecognizer

I've created a UIGestureRecognizer much like this one:
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [gestureRecognizer scale];
}
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 2.0;
const CGFloat kMinScale = 1.0;
CGFloat newScale = 1 - (lastScale - [gestureRecognizer scale]);
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
[gestureRecognizer view].transform = transform;
lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
This works as expected, however my client wants it to be less sensitive to touch. How can I reduce the velocity of the pinching (both inward and outward) so that it zooms at about 80% the default velocity?
Did you tried to scale the current value to the 80%.
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat maxScale = 2.0;
CGFloat currentScale = [gestureRecognizer scale];
currentScale = 0.8 * currentScale; //80% of scaling
if(currentScale < 0.8)
currentScale = 0.8;
if(currentScale > maxScale)
currentScale = maxScale;
[gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], currentScale, currentScale);
}
Try this.
- (void)handlePinching:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
if (CGRectIsEmpty(self.initalFrame)) {
self.initalFrame = gestureRecognizer.view.frame;
// store view's original frame and never change it
}
if (self.preScale == 0.f) {
self.preScale = 1.f;
}
gestureRecognizer.scale = self.preScale;
// gestureRecognizer and view should share the same scale state at the beginning
}
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
const CGFloat kMaxScale = 2.0;
const CGFloat kMinScale = 1.0;
CGFloat newScale = gestureRecognizer.scale;
newScale = (newScale - self.preScale) * 0.8 + self.preScale;
newScale = MIN(newScale, kMaxScale);
newScale = MAX(newScale, kMinScale);
CGRect newFrame = self.initalFrame;
newFrame.size.height *= newScale;
newFrame.size.width *= newScale;
gestureRecognizer.view.frame = newFrame;
self.preScale = newScale;
}
}
The points are
use frame to implement scale.
do change to the scale variable's change to slow/speed scaling.
Turns out the excessive speed of the zoom was actually a bug in the linked answer's code. Sadly this doesn't actually answer my actual question though (for which I'd still like an answer!), but it does serve to solve my client's problem.
Note the added line at the bottom that resets the gestureRecognizer back to 1:
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer {
...
lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
gestureRecognizer.scale = 1;
// ^ added this line
}
}

UIPinchGestureRecognizer inwards pinch "slower"

I'm using UIPinchGestureRecognizer in my app to zoom in on a view (and yes, there's a reason I'm not using UIScrollView). When I pinch outwards with my fingers, the view zooms in as expected, and if I then reverse pinch without taking my fingers off the screen, it also zooms right. However, if I initiate the zoom by pinching inwards, the rate at which the view zooms is dramatically slower. I'm guessing this is because of how UIPinchGestureRecognizer works - the scale of the UIPinchGestureRecognizer is >1 when pinching outwards, and <1 when pinching inwards. Unfortunately, I do not know how to accurately reflect this in my code.
- (IBAction)didDetectPinchGesture:(id)sender {
UIPinchGestureRecognizer *gestureRecognizer = (UIPinchGestureRecognizer *)sender;
CGFloat scale = [gestureRecognizer scale];
switch ([gestureRecognizer state]) {
case UIGestureRecognizerStateBegan:
_lastScale = [gestureRecognizer scale];
break;
case UIGestureRecognizerStateChanged:
CGFloat currentScale = [[self.imageView.layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 5.0;
const CGFloat kMinScale = 1.0;
CGFloat newScale = 1 - (_lastScale - scale); // new scale is in the range (0-1)
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
NSLog(#"%f", newScale);
CGAffineTransform transform = CGAffineTransformScale([self.imageView transform], newScale, newScale);
self.imageView.transform = transform;
_lastScale = scale; // Store the previous scale factor for the next pinch gesture call
break;
default:
_lastScale = [gestureRecognizer scale];
break;
}
}
A very simple solution to this is to reset the gestureRecognizer scale back to 1 when you're finished:
...
default:
_lastScale = [gestureRecognizer scale];
// Add this:
[gestureRecognizer setScale:1];
break;
}

Resources