29

Do I actually need a UIPinchGestureRecognizer inside a UIScrollView to have the pinch working? If yes how do I do it? I am trying to implement what flipboard has, where it basically zooms in an image and have the scroll capability after zooming in. How do I do that?

UPDATE:

Here's some code that I have which doesn't call the scroll view delegate

CGRect imgFrame;
imgFrame.size.width = originalImageSize.width;
imgFrame.size.height = originalImageSize.height;
imgFrame.origin.x = imageOriginPoint.x;
imgFrame.origin.y = imageOriginPoint.y;

NSData *data = [request responseData];
UIImage * image = [UIImage imageWithData:data];
imageView = [[UIImageView alloc] initWithImage:image];
[imageView setUserInteractionEnabled:YES];
[imageView setBackgroundColor:[UIColor clearColor]];
[imageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[imageView setFrame:CGRectMake(0, 0, imgFrame.size.width, imgFrame.size.height)];

UIScrollView * imgScrollView = [[UIScrollView alloc] initWithFrame:imageView.frame];
imgScrollView.delegate = self;
imgScrollView.showsVerticalScrollIndicator = NO;
imgScrollView.showsHorizontalScrollIndicator = NO;
[imgScrollView setScrollEnabled:YES];
[imgScrollView setClipsToBounds:YES];
[imgScrollView addSubview:imageView];
[imgScrollView setBackgroundColor:[UIColor blueColor]];
[imgScrollView setMaximumZoomScale:1.0];
pkamb
  • 33,281
  • 23
  • 160
  • 191
adit
  • 32,574
  • 72
  • 229
  • 373

6 Answers6

96

All you need to do is add your UIImageView (or any view you want to zoom) inside your UIScrollView.

Set your maximumZoomScale on your UIScrollView to any value higher than 1.0f.

Set yourself as the delegate of your UIScrollView and return the UIImageView in the viewForZooming delegate method.

That's it. No pinch gesture needed, no nothing. UIScrollView handles pinch zooming for you.

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Ignacio Inglese
  • 2,605
  • 16
  • 18
  • yea, I did all that but the delegate is not called – adit Jan 17 '12 at 16:46
  • aha! I didn't set the maxZoomScale and therefore it didn't work! thanks – adit Jan 17 '12 at 16:49
  • 1
    Ignacio please can you tell the constraints on imageView and scrollView. I did the same but it is not working. My image view is set to aspect fill and width are going outside from the image View frame so I need to increase image view frame as well as scroll view content size width. I have only one image in my scroll view – Muhammad Sarim Nov 09 '17 at 10:03
  • in case you need zoom out feature, just set the minimumZoomScale property as well. – Murat Yasar Jul 09 '19 at 18:39
  • Also note when adding the UIImageView to the scroll view, don't set any layout constraint on it. – CodeBrew May 14 '22 at 03:20
20

Objective C

Pinch-zoom Image in Scrollview Steps (Objective C)

1. Scroll View Constraints

enter image description here

2. Add ImageView In ScrollView and set Constraints

enter image description here

3. Take IBOutlets

IBOutlet UIScrollView * bgScrollView;
IBOutlet UIImageView * imageViewOutlet;

4. viewDidLoad Method

- (void)viewDidLoad
 {
    [super viewDidLoad];

    float minScale=bgScrollView.frame.size.width / imageViewOutlet.frame.size.width;
    bgScrollView.minimumZoomScale = minScale;
    bgScrollView.maximumZoomScale = 3.0;
    bgScrollView.contentSize = imageViewOutlet.frame.size;
    bgScrollView.delegate = self;
}

5. Scroll View Delegate Method

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

Swift

Pinch-zoom Image in Scrollview Steps (Swift)

1. Scroll View Constraints

enter image description here

2. Add ImageView In ScrollView and set Constraints

enter image description here

3. Take IBOutlets

@IBOutlet weak var bgScrollView : UIScrollView!
@IBOutlet weak var imageView : UIImageView!

4. viewDidLoad Method

 override func viewDidLoad() {
    super.viewDidLoad()

    let minScale = bgScrollView.frame.size.width / imageView.frame.size.width;
    bgScrollView.minimumZoomScale = minScale
    bgScrollView.maximumZoomScale = 3.0
    bgScrollView.contentSize = imageView.frame.size
    bgScrollView.delegate = self
}

5. Scroll View Delegate Method

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imageView
}
Shrikant Tanwade
  • 1,391
  • 12
  • 21
14

I did a custom image viewer not a long ago without the pinch recognizers. Just UIImageView on top of UIScrollView. There you pass a string with a link to the image and it also has a progress bar. Once that image is finished loading the image is shown. Here's the code:

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
  return self.theImageView;
}

- (CGRect)centeredFrameForScrollView:(UIScrollView *)scroll andUIView:(UIView *)rView {
  CGSize boundsSize = scroll.bounds.size;
  CGRect frameToCenter = rView.frame;
  // center horizontally
  if (frameToCenter.size.width < boundsSize.width) {
    frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
  }
  else {
    frameToCenter.origin.x = 0;
  }
  // center vertically
  if (frameToCenter.size.height < boundsSize.height) {
    frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
  }
  else {
    frameToCenter.origin.y = 0;
  }
  return frameToCenter;
}

-(void)scrollViewDidZoom:(UIScrollView *)scrollView
{
  self.theImageView.frame = [self centeredFrameForScrollView:self.theScrollView andUIView:self.theImageView];                               
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  [self.resourceData setLength:0];
  self.filesize = [NSNumber numberWithLongLong:[response expectedContentLength]];
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  [self.resourceData appendData:data];
  NSNumber *resourceLength = [NSNumber numberWithUnsignedInteger:[self.resourceData length]];
  self.progressBar.progress = [resourceLength floatValue] / [self.filesize floatValue];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  self.theImage = [[UIImage alloc]initWithData:resourceData];
  self.theImageView.frame = CGRectMake(0, 0, self.theImage.size.width, self.theImage.size.height);
  self.theImageView.image = self.theImage;
  self.theScrollView.minimumZoomScale = self.theScrollView.frame.size.width / self.theImageView.frame.size.width;
  self.theScrollView.maximumZoomScale = 2.0;
  [self.theScrollView setZoomScale:self.theScrollView.minimumZoomScale];
  self.theScrollView.contentSize = self.theImageView.frame.size;
  self.theLabel.hidden = YES;
  self.progressBar.hidden = YES;
}

-(void)setImageInImageView
{
  NSURLRequest *req = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:self.imageLink]];
  NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:req delegate:self];
  if (conn)
  {
    self.resourceData = [NSMutableData data];
  }
  else
  {
    NSLog(@"Connection failed: IMageViewerViewController");
  }
}

-(void)loadView
{
  self.filesize = [[NSNumber alloc]init];
  self.progressBar = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
  self.progressBar.frame = CGRectMake(20, 240, 280, 40);
  [self.progressBar setProgress:0.0];
  self.theImageView = [[[UIImageView alloc]initWithFrame:[[UIScreen mainScreen]applicationFrame]]autorelease];
  self.theScrollView = [[[UIScrollView alloc]initWithFrame:[[UIScreen mainScreen]applicationFrame]]autorelease];
  self.theScrollView.delegate = self;
  [self.theScrollView addSubview:self.theImageView];
  self.view = self.theScrollView;
  self.theLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 200, 320, 40)];
  self.theLabel.font = [UIFont boldSystemFontOfSize:15.0f];
  self.theLabel.text = @"Please wait, file is being downloaded";
  self.theLabel.textAlignment = UITextAlignmentCenter;
  self.theLabel.hidden = NO;
  [self.view addSubview:self.progressBar];
  [self.view bringSubviewToFront:self.progressBar];
  [self.view addSubview:self.theLabel];
  [self.view bringSubviewToFront:self.    theLabel];
  [self performSelectorOnMainThread:@selector(setImageInImageView) withObject:nil waitUntilDone:NO];
}

And the header file:

@interface ImageViewerViewController : UIViewController<UIScrollViewDelegate, NSURLConnectionDelegate, NSURLConnectionDataDelegate>

@property (nonatomic, retain) IBOutlet UIImageView *theImageView;
@property (nonatomic, retain) IBOutlet UIScrollView *theScrollView;
@property (nonatomic, retain) NSString *imageLink;
@property (nonatomic, retain) UIImage *theImage;
@property (nonatomic, retain) UILabel *theLabel;
@property (nonatomic, retain) UIProgressView *progressBar;
@property (nonatomic, retain) NSMutableData *resourceData;
@property (nonatomic, retain) NSNumber *filesize;
@end

Hope it helps

Novarg
  • 7,390
  • 3
  • 38
  • 74
  • do you know why that viewForZoomingInScrollView is never called? – adit Jan 17 '12 at 08:21
  • @adit it is called by the UIScrollViewDelegate I believe – Novarg Jan 17 '12 at 08:24
  • This is basically a reformed version of the zoomingPDFViwer by the looks of it, it is better. I have a question for @Novarg. When I load PDF this why from a tableview onto a scrollview it downloads but does not update the scrollview the first time it is selected but works on the second time why is this? – Popeye Jan 17 '12 at 10:41
  • @mattInman it might be pretty much the same as zoomingPDFViewer, I just gathered a lot of code and then used pieces from these codes to create my class as I want it. And sorry, I didn't understand your question. Could you be more specific please? And this class was made to show images(jpg/jpeg/gif/bmp/png/etc.), so I'm not sure it will work with PDFs. – Novarg Jan 17 '12 at 10:50
  • @Novarg Its OK i found my answer. – Popeye Jan 18 '12 at 08:19
  • @Novarg I've tried your code.I'm having trouble to load image everything is working perfectly but not able to show my Image.(just black screen)Any idea how? – Vaibhav Limbani Aug 13 '14 at 05:23
9

I guess this answer may be unnecessary but I have had similar problems as @adit and solved it in a very simple way (I think) and maybe someone with similar challenges can use it:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor yellowColor];

    UIImage *imageToLoad = [UIImage imageNamed:@"background_green"];

    self.myImageView = [[UIImageView alloc]initWithImage:imageToLoad];
    self.myScrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
    [self.myScrollView addSubview:self.myImageView];
    self.myScrollView.contentSize = self.myImageView.bounds.size;
    self.myScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    self.myScrollView.minimumZoomScale = 0.3f;
    self.myScrollView.maximumZoomScale = 3.0f;
    self.myScrollView.delegate = self;
    [self.view addSubview:self.myScrollView];
}

- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView {
    NSLog(@"viewForZoomingInScrollView");
    return self.myImageView;
} 

My problem was a very simple I had forgot to add self.myScrollView.delegate = self;, which was the reason why i had problems. It took me forever to figure that simple problem out, i guess I did not see the forrest for all the trees :-)

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
PeterK
  • 4,243
  • 4
  • 44
  • 74
2

When I was developing my Zooming PDF viewer in a UIScrollView I found out that when running it on the Phone it actually already has the zooming implemented as part of the phone. But you could have a look at this, http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html it is a piece of sample code that tries to implement the functionality for zooming, this is want got me started.

Popeye
  • 11,839
  • 9
  • 58
  • 91
  • I actually looked at that and it says that I will need a delegate called viewForZoomingInScrollView, however this is never called although I already set the delegate to self – adit Jan 17 '12 at 08:15
  • 1
    Like @Novarg hassaid it gets called by the UIScrollViewDelegate so in your .h file make sure to add – Popeye Jan 17 '12 at 10:42
0

For 'snap-image-to-screen-edges' solution @Shrikant Tanwade's worked well, but I wanted some insets. I tried settings constraints' constants, but that does not worked.

I ended with following solution:

  1. same as for Shrikant's - set scrollView, imageView's constraints, set their constants to 0.

  2. set insets with scrollView.contentInset = UIEdgeInsetsMake(....

medvedNick
  • 4,512
  • 4
  • 32
  • 50