Zoomable image view for iPhone in Xcode 4.6

When developing an iPhone app I was recently presented with the challenge of displaying an image and allow for it to be zoomed in and out on while also allowing for a scroll function, up, down and side to side. To accomplish this I used xcode and created my xib with an UIImageView which I placed inside of a UIScrollView like so.

Scrollable image view

 

I wish that was it, but unfortunately there is a bit more to do. We need to add some code to handle the zoom and scroll functions for us. I have compiled and modified this code from various sources, mostly from stackoverflow and I want to give partial credit to this guide which may also help you if my guide is not sufficent.

We will be using the image view and scroll view delegates in our .m file so we declare them in the header file like so.

//
//  ImageViewController.h
//  sfsfum
//
//  Created by MarkusTenghamn on 3/10/13.
//  Copyright (c) 2013 MarkusTenghamn. All rights reserved.
//
 
#import <UIKit/UIKit.h>

@interface ImageViewController : UIViewController
 
@property (strong, nonatomic) IBOutlet UIImageView *imgView;
@property (strong, nonatomic) NSString *imgPath;
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
 
- (IBAction)BackBtnPress:(id)sender;
 
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
 
@end

 

Our .m file will handle all of the touch gestures and the manipulation of the image. I will display my full code here as I believe it is more helpful. Please note that I declare the image path when creating the view outside of this class, see an example of this at the end of the post.

//
//  ImageViewController.m
//  sfsfum
//
//  Created by MarkusTenghamn on 3/10/13.
//  Copyright (c) 2013 MarkusTenghamn. All rights reserved.
//
 
#import "ImageViewController.h"

@interface ImageViewController ()
 
@end
 
@implementation ImageViewController
 
@synthesize imgPath = _imgPath;
@synthesize imgView = _imgView;
@synthesize scrollView = _scrollView;
 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}
 
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imgView;
}
 
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSString *path = [[NSBundle mainBundle] pathForResource:self.imgPath ofType:@"png"];
    UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
    self.imgView.image = image;
    self.imgView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
    [self.scrollView addSubview:self.imgView];
    self.scrollView.contentSize = image.size;
 
    CGRect scrollViewFrame = self.scrollView.frame;
    CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
    CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
    CGFloat minScale = MIN(scaleWidth, scaleHeight);
    self.scrollView.minimumZoomScale = minScale;
    self.scrollView.maximumZoomScale = 1.5f;
    self.scrollView.zoomScale = minScale;
 
    [self centerScrollViewContents];
}
 
- (void)viewDidLoad
{
    [super viewDidLoad];
    NSString *path = [[NSBundle mainBundle] pathForResource:self.imgPath ofType:@"png"];
    UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
    self.imgView = [[UIImageView alloc] initWithImage:image];
 
    UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewDoubleTapped:)];
    doubleTapRecognizer.numberOfTapsRequired = 2;
    doubleTapRecognizer.numberOfTouchesRequired = 1;
    [self.scrollView addGestureRecognizer:doubleTapRecognizer];
 
    UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewTwoFingerTapped:)];
    twoFingerTapRecognizer.numberOfTapsRequired = 1;
    twoFingerTapRecognizer.numberOfTouchesRequired = 2;
    [self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
}
 
- (void)centerScrollViewContents {
    CGSize boundsSize = self.scrollView.bounds.size;
    CGRect contentsFrame = self.imgView.frame;
 
    if (contentsFrame.size.width < boundsSize.width) {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
    } else {
        contentsFrame.origin.x = 0.0f;
    }
 
    if (contentsFrame.size.height < boundsSize.height) {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
    } else {
        contentsFrame.origin.y = 0.0f;
    }
 
    self.imgView.frame = contentsFrame;
}
 
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
    // 1
    CGPoint pointInView = [recognizer locationInView:self.imgView];
 
    // 2
    CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
    newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
 
    // 3
    CGSize scrollViewSize = self.scrollView.bounds.size;
 
    CGFloat w = scrollViewSize.width / newZoomScale;
    CGFloat h = scrollViewSize.height / newZoomScale;
    CGFloat x = pointInView.x - (w / 2.0f);
    CGFloat y = pointInView.y - (h / 2.0f);
 
    CGRect rectToZoomTo = CGRectMake(x, y, w, h);
 
    // 4
    [self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
 
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
    // Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
    CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
    newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
    [self.scrollView setZoomScale:newZoomScale animated:YES];
}
 
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    // The scroll view has zoomed, so you need to re-center the contents
    [self centerScrollViewContents];
}
 
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
 
- (IBAction)BackBtnPress:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}
@end

 

Also note that I use png files for my app but you can simply change the extension to any other extension which is supported. To call the image view I use the following code.

    ImageViewController *view = [ImageViewController alloc];
    view.imgPath = [[self.menus objectAtIndex:[indexPath row]] objectForKey:@"PATH"];
    [self presentViewController:view animated:YES completion:nil];

 

I hope that helps anyone else struggling with this, if you have any questions please do let me know!

About Markus Tenghamn

avatar

Markus Tenghamn is a programmer and entrepreneur currently studying computer science at Mälardalens University (MDH), Sweden, in addition to being a big part of several new and innovative startups. Markus has pursued web development for over 10 years and started his first ecommerce website at the age of 17. At the moment Markus is responsible for developing MDH Schedule iPhone app (unofficial) and sync server which let's students see their course schedules right on their phone. In addition to other projects, he is a volunteer for SFSFUM (Meeting for student unions in Sweden) where he developed an app for iPhone, Android and Windows Phone 7 which gives participants maps with direction and event information. He has also been a finalist for the Rising Star scholarship awarded by Östsvenska Handelskammaren for course projects at MDH.


Markus's latest startup is called Anveto AB (Anveto.com), an innovative analytics and marketing platform which will bring truly unique and new ideas to companies looking to increase revenue.


In his spare time Markus prefers to write about web development, app development and startups on his blog at MarkusTenghamn.com. When he is not doing that he loves to read about AI, chaos theory, space and quantum mechanics.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

Scroll To Top
OR
SUBSCRIBE!
I will send you a quick email whenever I post something interesting! you can opt out at any time by clicking a link and I never give your information to third parties.
WANT MORE WEB DEV POSTS?
WANT MORE FREEBIES? SUBSCRIBE!
WANT MORE GAMING POSTS?
WANT MORE IOS DEV POSTS?
WANT MORE ANDROID DEV POSTS?
STAY UPDATED!
GET THE LATEST POSTS VIA EMAIL!
GET MY LATEST REVIEWS!