使用PinchGesture;如何放大用户手指的实际位置"捏"?

时间:2014-09-16 00:47:54

标签: ios objective-c uipinchgesturerecognizer

我已经在我的应用程序中的UIImageView上实现了UIPinchGestureRecognizer,但无论我在哪里捏合图像,它似乎都放大到相同的位置。有谁知道如何让它放大到用户实际“捏”的位置?请参阅下面的代码。

ViewController.m

 - (IBAction)scaleImage:(UIPinchGestureRecognizer *)recognizer {

   recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
   recognizer.scale = 1; 

 }

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
{
    BOOL shouldReceiveTouch = YES;

    if (gestureRecognizer == tap) {
        shouldReceiveTouch = (touch.view == featureImage);
    }
    return shouldReceiveTouch;
}

5 个答案:

答案 0 :(得分:31)

缩放变换使原点(0,0)保持不变。因此,要缩放特定点周围的视图,必须首先将该点转换为原点,然后应用比例,然后转换回来。

- (IBAction)pinchGestureDidFire:(UIPinchGestureRecognizer *)pinch {

首先,我们得到视图被压缩。

    UIView *pinchView = pinch.view;

要计算夹点的中心,我们需要视图边界的中点,所以我们也得到了界限:

    CGRect bounds = pinchView.bounds;

中心是基于捏的触摸的质心,我们这样得到:

    CGPoint pinchCenter = [pinch locationInView:pinchView];

但实际上我们需要相对于视图中心的夹点偏移,因为默认情况下视图的变换相对于视图的中心。 (您可以通过更改视图layer.anchorPoint来更改此设置。)

    pinchCenter.x -= CGRectGetMidX(bounds);
    pinchCenter.y -= CGRectGetMidY(bounds);

现在我们可以更新视图的转换。首先,我们得到它当前的变换:

    CGAffineTransform transform = pinchView.transform;

然后我们更新它以将夹点中心转换为原点:

    transform = CGAffineTransformTranslate(transform, pinchCenter.x, pinchCenter.y);

现在我们可以应用比例:

    CGFloat scale = pinch.scale;
    transform = CGAffineTransformScale(transform, scale, scale);

然后我们将视图翻译回来:

    transform = CGAffineTransformTranslate(transform, -pinchCenter.x, -pinchCenter.y);

现在我们可以使用修改后的转换更新视图:

    pinchView.transform = transform;

最后,我们重置了手势识别器的比例,因为我们已经应用了当前比例:

    pinch.scale = 1.0;
}

演示:

pinch scale

请注意,在模拟器中,您可以为捏合手势保留选项(alt)。按住shift(按住选项)可将两个触摸移动到一起。

以下是复制/粘贴的所有代码:

- (IBAction)pinchGestureDidFire:(UIPinchGestureRecognizer *)pinch {
    UIView *pinchView = pinch.view;
    CGRect bounds = pinchView.bounds;
    CGPoint pinchCenter = [pinch locationInView:pinchView];
    pinchCenter.x -= CGRectGetMidX(bounds);
    pinchCenter.y -= CGRectGetMidY(bounds);
    CGAffineTransform transform = pinchView.transform;
    transform = CGAffineTransformTranslate(transform, pinchCenter.x, pinchCenter.y);
    CGFloat scale = pinch.scale;
    transform = CGAffineTransformScale(transform, scale, scale);
    transform = CGAffineTransformTranslate(transform, -pinchCenter.x, -pinchCenter.y);
    pinchView.transform = transform;
    pinch.scale = 1.0;
}

答案 1 :(得分:8)

迅速实施抢劫答案:

@objc private func pinchHandler(gesture: UIPinchGestureRecognizer) {
    if let view = gesture.view {

        switch gesture.state {
        case .changed:
            let pinchCenter = CGPoint(x: gesture.location(in: view).x - view.bounds.midX,
                                      y: gesture.location(in: view).y - view.bounds.midY)
            let transform = view.transform.translatedBy(x: pinchCenter.x, y: pinchCenter.y)
                                            .scaledBy(x: gesture.scale, y: gesture.scale)
                                            .translatedBy(x: -pinchCenter.x, y: -pinchCenter.y)
            view.transform = transform
            gesture.scale = 1
        case .ended:
            // Nice animation to scale down when releasing the pinch.
            // OPTIONAL
            UIView.animate(withDuration: 0.2, animations: {
                view.transform = CGAffineTransform.identity
            })
        default:
            return
        }


    }
}

答案 2 :(得分:0)

你可以简单地添加一个scrollView,并在scrollView中添加一个imageView,如下所示,

![ImageView on top of scrollView][1]

图片上传存在一些问题,请在此link

查看图片

然后,您可以创建scrollView和imageView的IBOutlet属性,并分别连接它。稍后,在viewDidAppear方法

中添加这些行
_scrollView.maximumZoomScale = 10.0;        //Maximum Zoom
_scrollView.minimumZoomScale = minimumScale;   //Minimum Zoom

另外添加以下方法,

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
   return _imageView;
}

现在你可以获得你设置的放大/缩小效果和最大/最小缩放限制。较少的代码以及实现你的目标。

答案 3 :(得分:0)

Rob Mayoff(@robmayoff)的回答确实令人鼓舞,我希望我早点找到它。但是我实际上是通过更改锚点而不是使用转换转换来解决的。实际上,它具有相同的结果。

我还没有在SO上看到这种解决方案(但...),并认为这是个好地方。

相对于视图层锚点转换工作。但是,如果视图已转换,则不能仅将位置点设置为收缩中心点。因此,这是一种设置图层锚点并正确转换位置点的方法:

// change the anchor point for the view layer
- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view {
    // sanity check - x and y MUST be between 0 and 1
    if (anchorPoint.x < 0 || anchorPoint.x > 1 ||
        anchorPoint.y < 0 || anchorPoint.y > 1) {
        return;
    }

    CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
                                   view.bounds.size.height * anchorPoint.y);
    CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
                                   view.bounds.size.height * view.layer.anchorPoint.y);

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

    CGPoint position = view.layer.position;

    position.x -= oldPoint.x;
    position.x += newPoint.x;

    position.y -= oldPoint.y;
    position.y += newPoint.y;

    view.layer.position = position;
    view.layer.anchorPoint = anchorPoint;
}

这是handlePinch方法,它使用setAnchorPoint方法将图层锚点设置为收缩接触点的中心点:

- (void)handlePinch:(UIPinchGestureRecognizer *)recognizer {
    // make these static so they can be used across gesture states
    static CGAffineTransform initialTransform;
    static CGPoint initialAnchor;

    if (recognizer.state == UIGestureRecognizerStateBegan) {
        // save these for later states
        initialTransform = recognizer.view.transform;
        initialAnchor = recognizer.view.layer.anchorPoint;

        // get the center point of the pinch
        CGPoint touch = [recognizer locationInView:recognizer.view];

        // anchor point is relative to the view bounds:  0 ... up to 1.0, for both x and y
        CGFloat anchorX = touch.x / recognizer.view.bounds.size.width;
        CGFloat anchorY = touch.y / recognizer.view.bounds.size.height;

        // set the layer anchor point AND position, to where the view was initially pinched
        [self setAnchorPoint:CGPointMake(anchorX,anchorY) forView:recognizer.view];

    } else if (recognizer.state == UIGestureRecognizerStateChanged) {
        // perform the pinch zoom
        recognizer.view.transform = CGAffineTransformScale(initialTransform,recognizer.scale,recognizer.scale);

    } else if (recognizer.state == UIGestureRecognizerStateEnded) {
        // reset the scale when it's done
        recognizer.scale = 1;

        // restore the original anchor point
        [self setAnchorPoint:initialAnchor forView:recognizer.view];
    }
}

答案 4 :(得分:-1)

ViewController.h

@property (strong, nonatomic) IBOutlet UIImageView *img;

ViewController.m

- (void)viewDidLoad

 {

    [super viewDidLoad];

    UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchDetected:)];

    [_img addGestureRecognizer:pinchRecognizer];

    // Do any additional setup after loading the view, typically from a nib.

}

- (void)pinchDetected:(UIPinchGestureRecognizer *)pinchRecognizer

{

    CGFloat scale = pinchRecognizer.scale;

    _img.transform = CGAffineTransformScale(self.img.transform, scale, scale);

    pinchRecognizer.scale = 1.0;
}