当用户用手指捏屏幕时,如何放大/缩小UIImage对象?

84

当用户在我的应用程序上执行标准的捏合操作时,我希望能够放大/缩小UIImage对象。目前,我正在使用UIImageView来显示我的图像,如果这个细节有所帮助的话。

我正在尝试弄清楚如何实现这一点,但迄今为止没有成功。

有什么线索吗?

9个回答

94

如其他人所描述的那样,最简单的解决方案是将你的UIImageView放入一个UIScrollView中。我在Interface Builder的.xib文件中完成了这个操作。

在viewDidLoad中,设置下列变量。将你的控制器设为UIScrollViewDelegate。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.scrollView.minimumZoomScale = 0.5;
    self.scrollView.maximumZoomScale = 6.0;
    self.scrollView.contentSize = self.imageView.frame.size;
    self.scrollView.delegate = self;
}
你需要实现以下方法来返回你想要缩放的imageView。
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}
在iOS9之前的版本中,您可能还需要添加这个空的代理方法:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
}

苹果文档描述得非常好,可以帮助您完成此操作:


3
现在可以删除scrollViewDidEndZooming:withView:了,我已经在iOS9 Xcode7上进行了测试。 - ChenYilong
1
这对我有效!没有实现 scrollViewDidEndZooming: 方法。 - Johnny

85
另一种简单的方法是将你的 UIImageView 放置在一个 UIScrollView 中。如我在这里所述,需要将滚动视图的 contentSize 设置为与你的 UIImageView 的大小相同。将你的控制器实例设置为滚动视图的委托,并实现 viewForZoomingInScrollView:scrollViewDidEndZooming:withView:atScale: 方法以允许捏合缩放和图片平移。这基本上就是 Ben 的解决方案所做的事情,只是以稍微更轻量级的方式,因为你没有完整 Web 视图的开销。
你可能会遇到的一个问题是,滚动视图内的缩放是通过应用于图像的变换来实现的。这可能会导致高缩放因子下的模糊。对于可以重新绘制的内容,你可以按照我的建议 这里 提供的方法,在捏合手势完成后提供更清晰的显示。然后可以使用 hniels 的解决方案来重新缩放你的图片。

2
确保您更改maximumZoomScale和/或minimumZoomScale :). - Chris Prince

47

Shefali提供的UIImageView解决方案非常好,但需要进行一些修改:

- (void)pinch:(UIPinchGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded
        || gesture.state == UIGestureRecognizerStateChanged) {
        NSLog(@"gesture.scale = %f", gesture.scale);

        CGFloat currentScale = self.frame.size.width / self.bounds.size.width;
        CGFloat newScale = currentScale * gesture.scale;

        if (newScale < MINIMUM_SCALE) {
            newScale = MINIMUM_SCALE;
        }
        if (newScale > MAXIMUM_SCALE) {
            newScale = MAXIMUM_SCALE;
        }

        CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
        self.transform = transform;
        gesture.scale = 1;
    }
}

Shefali的解决方案的缺点在于,在捏合时它不能连续缩放。此外,当开始一个新的捏合时,当前图像的比例尺被重置。


我想在捏合手势开始后启用平移手势。如何做到这一点? - Gajendra K Chauhan
@Gajendra:在这种情况下,我认为你应该使用滚动视图来嵌入你的组件,它可以直接支持。 - JRV
但我想要同时支持点击手势和捏合手势的功能。我还在ImageView上使用了网格(层)。请问,在ScrollView中是否可以同时使用捏合手势和点击手势? - Gajendra K Chauhan
@GajendraKChauhan:滚动视图支持开箱即用的捏合缩放和平移功能 - 只需记住设置最小/最大缩放比例,但也要查看Brad Larsons的回答。如果您还想添加点击手势,也可以将其作为手势添加进去。 - JRV
@JRV,我需要添加哪些委托才能使self.frame.size.width被识别? - user4203956

22

以下代码可帮助在不使用 UIScrollView 的情况下缩放 UIImageView:

-(void)HandlePinch:(UIPinchGestureRecognizer*)recognizer{
    if ([recognizer state] == UIGestureRecognizerStateEnded) {
        NSLog(@"======== Scale Applied ===========");
        if ([recognizer scale]<1.0f) {
            [recognizer setScale:1.0f];
        }
        CGAffineTransform transform = CGAffineTransformMakeScale([recognizer scale],  [recognizer scale]);
        imgView.transform = transform;
    }
}

这是最优秀的解决方案 :). 谢谢。 - sabiland

18

请记住,您永远不要对UIImage进行缩放。

相反,您要对显示UIImageview进行缩放。

在这种情况下,您可以选择创建一个自定义的UIView,使用自定义绘图来显示图像,或者使用为您显示图像的UIImageViewUIWebView(后者需要一些额外的HTML支持)。

无论哪种情况,您都需要实现touchesBegantouchesMoved等方法来确定用户想做什么(缩放,平移等)。


+1 为澄清,从没想过那样,干杯,伙计。 - Elmo
@August,你能给我提供一个在touchesBegan和touchesMoved时缩放和旋转图像的例子吗? - Mitesh Dobareeya

7

这里是我之前使用过的一种解决方案,不需要使用UIWebView。

- (UIImage *)scaleAndRotateImage(UIImage *)image
{
    int kMaxResolution = 320; // Or whatever

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);


    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = bounds.size.width / ratio;
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = bounds.size.height * ratio;
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient) {

        case UIImageOrientationUp: //EXIF = 1
            transform = CGAffineTransformIdentity;
            break;

        case UIImageOrientationUpMirrored: //EXIF = 2
            transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            break;

        case UIImageOrientationDown: //EXIF = 3
            transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationDownMirrored: //EXIF = 4
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
            transform = CGAffineTransformScale(transform, 1.0, -1.0);
            break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationLeft: //EXIF = 6
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationRightMirrored: //EXIF = 7
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeScale(-1.0, 1.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        case UIImageOrientationRight: //EXIF = 8
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

    }

    UIGraphicsBeginImageContext(bounds.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
        CGContextScaleCTM(context, -scaleRatio, scaleRatio);
        CGContextTranslateCTM(context, -height, 0);
    }
    else {
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextTranslateCTM(context, 0, -height);
    }

    CGContextConcatCTM(context, transform);

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}

这篇文章可以在Apple支持网站上找到: http://discussions.apple.com/message.jspa?messageID=7276709#7276709

-1 用于 JSP 托管支持页面。对点语法摇拳 - esharp
请问您如何调用这个方法(在鼠标移动或触摸时)? - Bhumesh Purohit

5

Shafali和JRV的答案扩展包括平移和捏合缩放:

#define MINIMUM_SCALE 0.5
#define MAXIMUM_SCALE 6.0
@property CGPoint translation;


- (void)pan:(UIPanGestureRecognizer *)gesture {
    static CGPoint currentTranslation;
    static CGFloat currentScale = 0;
    if (gesture.state == UIGestureRecognizerStateBegan) {
        currentTranslation = _translation;
        currentScale = self.view.frame.size.width / self.view.bounds.size.width;
    }
    if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateChanged) {

        CGPoint translation = [gesture translationInView:self.view];

        _translation.x = translation.x + currentTranslation.x;
        _translation.y = translation.y + currentTranslation.y;
        CGAffineTransform transform1 = CGAffineTransformMakeTranslation(_translation.x , _translation.y);
        CGAffineTransform transform2 = CGAffineTransformMakeScale(currentScale, currentScale);
        CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
        self.view.transform = transform;
    }
}


- (void)pinch:(UIPinchGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateChanged) {
//        NSLog(@"gesture.scale = %f", gesture.scale);

        CGFloat currentScale = self.view.frame.size.width / self.view.bounds.size.width;
        CGFloat newScale = currentScale * gesture.scale;

        if (newScale < MINIMUM_SCALE) {
            newScale = MINIMUM_SCALE;
        }
        if (newScale > MAXIMUM_SCALE) {
            newScale = MAXIMUM_SCALE;
        }

        CGAffineTransform transform1 = CGAffineTransformMakeTranslation(_translation.x, _translation.y);
        CGAffineTransform transform2 = CGAffineTransformMakeScale(newScale, newScale);
        CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
        self.view.transform = transform;
        gesture.scale = 1;
    }
}

请避免提到其他人的答案,因为他们随时可以删除它们。 - Enamul Hassan

3

如果你只想要捏合缩放,最简单的方法是将图像放在UIWebView中(编写少量html包装器代码,引用你的图像,基本上就完成了)。更复杂的方法是使用touchesBegantouchesMovedtouchesEnded来跟踪用户的手指,并相应地调整你的视图的变换属性。


你能否给我提供一些与你的答案相关的示例?如何使用touchesBegan、touchesMoved方法缩放和旋转图像。我的问题链接=> http://stackoverflow.com/questions/36833841/how-to-handle-multiple-touch-gesture-at-a-time?noredirect=1#comment61239098_36833841 - Mitesh Dobareeya

0

请记住,您不想放大/缩小UIImage。相反,请尝试放大/缩小包含UIImage View Controller的视图。

我已经为这个问题提供了一个解决方案。看看我的代码:

@IBAction func scaleImage(sender: UIPinchGestureRecognizer) {
        self.view.transform = CGAffineTransformScale(self.view.transform, sender.scale, sender.scale)
        sender.scale = 1
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        view.backgroundColor = UIColor.blackColor()
    }

注意:不要忘记连接PinchGestureRecognizer。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接