Mobinius Technologies Pvt. Ltd.
May I help you?
May I help you?
Please leave a message with your contact details. Our representative will get in touch with you soon.

Image Gallery (photo gallery) tutorial for iOS applications

This article explains how to develop a beautiful image gallery, which will be a reusable component in many of your iOS applications.

Note for the Beginners:
Assumed that the iOS beginners will have basic understanding on objective c and iOS UI development. If not please follow the apple guide/sample code to begin from here:

Image Gallery features:

1. To show the gallery of the images: The set of given images are shown in a UIScrollView and user can swipe or use Next/Previous buttons for the navigation.
2. Swipe gesture to navigate the images
3. Zoom the image: By pinch gesture you can zoom the image, also use double tap to zoom in/out the images.
4. Cyclic navigation: The images are displayed in a cyclic fashion.

How to begin:

Download the complete source code from here and open it in Xcode (V 4.0 onwards). Build and run the application and you must be able to see/make it work with the above listed features. Swipe across left/right and see the image changes and the counter title updates. Also pinch and zoom the image.

Find Source Code here

How did it happen?
So far we have understood the functions of this sample code and how this looks. Now lets move ahead and see how this can be achievable. The below section takes you in-depth inside the sample code and explains how the gallery is implemented.

Make sure you are following up the project in your machine for the clear understanding.

The below screenshot depicts the Xcode editor screen with the ImageGallery project opened.

Screen Shot 2013-02-01 at 3.02.07 PM

MBGalleryViewController

MBGalleryViewController, a sub class of UIViewController is responsible for almost all logic of the Gallery implementation.

When you open the header file you will see the below :
---
@interface MBGalleryViewController : UIViewController
{
@private
NSMutableArray *galleryImages_;
NSInteger currentIndex_;
NSInteger previousPage_;
}
@property (nonatomic, retain) UIImageView *prevImgView; //reusable Imageview - always contains the previous image
@property (nonatomic, retain) UIImageView *centerImgView; //reusable Imageview - always contains the currently shown image
@property (nonatomic, retain) UIImageView *nextImgView; //reusable Imageview - always contains the next image image

@property(nonatomic, retain)NSMutableArray *galleryImages; //Array holding the image file paths
@property(nonatomic, retain)UIScrollView *imageHostScrollView; //UIScrollview to hold the images

@property (retain, nonatomic) IBOutlet UIButton *prevImage;
@property (retain, nonatomic) IBOutlet UIButton *nxtImage;

@property (retain, nonatomic) IBOutlet UILabel *counterTitle;//A label to update the title with the count of the image

@property (nonatomic, assign) NSInteger currentIndex;

//navigation buttons methood...
- (IBAction)nextImage:(id)sender;
- (IBAction)prevImage:(id)sender;

-(void)setCounterTitle:(UILabel *)counterTitle;

#pragma mark - image loading-
//Simple method to load the UIImage, which can be extensible -
-(UIImage *)imageAtIndex:(NSInteger)inImageIndex;
----

Also lets have a glance at the xib file where we create some UI components which are required for the gallery.

Screen Shot 2013-02-01 at 3.01.36 PM

Inside Gallery:

The UIScrollView class provides support for displaying content that is larger than the size of the window size. It enables users to scroll within that content by making swiping gestures, and to zoom in and back from portions of the content by making pinching gestures.

Our gallery app is built using the UIScrollView.

The UIScrollView doesn’t contain any content when its loaded from the xib file. We will add the content inside programmatically.

The UIScrollView contains three sub scrollviews, each scrollview internally contains an image view. Each sub scrollview helps to support the zoom feature of the individual image views. These three ImageViews are for representing the previous, current and next images. Whenever the gallery needs the new image to be drawn, we will reuse these three image views. So at any moment, the previous, current and next image views will have three images loaded, when user swipes and image views are loaded with the new image.

Below diagram depicts the structure inside the UIScrollView imageHostScrollView.

Screen Shot 2013-02-01 at 3.38.12 PM

The method “- (void)viewDidLoad” of MBGalleryViewController.m file has the code required to load the sub scrollviews and image views.

----
// setup the scrollview
// the scrollview holds 3 uiimageviews in a row, to make nice swipe possible
self.imageHostScrollView.contentSize = CGSizeMake(CGRectGetWidth(self.imageHostScrollView.frame)*3, CGRectGetHeight(self.imageHostScrollView.frame));

self.imageHostScrollView.delegate = self;

CGRect rect = CGRectZero;

rect.size = CGSizeMake(CGRectGetWidth(self.imageHostScrollView.frame), CGRectGetHeight(self.imageHostScrollView.frame));

// add prevView as first in line
UIImageView *prevView = [[UIImageView alloc] initWithFrame:rect];
self.prevImgView = prevView;

UIScrollView *scrView = [[UIScrollView alloc] initWithFrame:rect];
[self.imageHostScrollView addSubview:scrView];

// similarly add centerImgView and nextImgView

// center the scrollview to show the middle view only
[self.imageHostScrollView setContentOffset:CGPointMake(CGRectGetWidth(self.imageHostScrollView.frame), 0) animated:NO];

-----

As mentioned, the above snippet adds three sub views (of type UIScrollView) to the host Scroll View.
We have to tell the scroll view the size of the content within it, so that it knows how far it can scroll horizontally and vertically.
In our case we need the horizontal scroll only, we set the content size of the UIScrollView equal to three times width of the UIImageView. Vertically we don’t need the scroller, we set the content size height as that of height of the scroll view.

Also we set the content offset is always starting point of the middle image view, nothing but the width of the scrollview.

Swipe and update the images:

We are handling the page updation by using the UIScrollView delegate methods. Our controller class acts as the delegate of UIScrollView and hence we observe the swipe guesture.

The delegate method scrollViewDidEndDecelerating: gets called when the scrollview halt after the swipe. Here we will update the current page based on the swipe direction and updates the image view with the new image.

Important: Though the uiscrollview is dragged and hence the offset of scrollview is modified, we will bring back the offset back to center (original point). This way we make sure that the center image view is shown back.

-----
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender {
CGFloat pageWidth = sender.frame.size.width;
int page = floor((sender.contentOffset.x - pageWidth / 2) / pageWidth) + 1;

//incase we are still in same page, ignore the swipe action
if(previousPage_ == page) return;

if(sender.contentOffset.x >= sender.frame.size.width) {
//swipe left, go to next image
[self setRelativeIndex:1];

// center the scrollview to the center UIImageView
[self.imageHostScrollView setContentOffset:CGPointMake(CGRectGetWidth(self.imageHostScrollView.frame), 0) animated:NO];
}
else if(sender.contentOffset.x < sender.frame.size.width) { //swipe right, go to previous image [self setRelativeIndex:-1]; // center the scrollview to the center UIImageView [self.imageHostScrollView setContentOffset:CGPointMake(CGRectGetWidth(self.imageHostScrollView.frame), 0) animated:NO]; } ------

The circular indexing:

The below borrowed code intelligently handles the indexing, no worries on 'out of bounds' indexing errors.

// this implementation does a negative safe modulo operation, to compensate for this
// NSLog(@"MOD -1 m 5 = %d", -1 % 5); => -1
// NSLog(@"MOD -7 m 5 = %d", -7 % 5); => -2
// NSLog(@"MOD -7 m 5 = %d", (5 + (-7 % 5)) % 5); => 3
// see also http://stackoverflow.com/questions/989943/weird-objective-c-mod-behavior
#define safeModulo(x,y) ((y + x % y) % y)

So when the index is out of bounds – this macro returns the new index within the boundary, and the indexing is cyclic always. For example, a gallery with 8 images, when the user views the eighth image and again swipes right then we will show the very first image. Same for the left swipe too, when the index reaches zero, we show the last image.

Handling the zoom:

To enable the zoom we need to set the below properties to the UIScrollView:
scrView.minimumZoomScale = 0.5; //50% minimum
scrView.maximumZoomScale = 2.5; //250% maximum

We are setting the minimum and maximum zoom scale. A zoom scale of one means that the
content is displayed at normal size. A zoom scale below one shows the content zoomed out, we set the minimum zoom scale to 0.5, means 50% less the actual size of the content.

Also the scale more than one shows the content zoomed in, we set it as 2.5, means 250% zoom.

The delegate method of UIScrollView ‘- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;’ must be implemented in the delegate class, in our case
the delegate is MBGalleryViewController.

Here we need to return the view object which needs to scale/zoom. This is always ‘self.centerImgView’ image view.

---
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView; {
//incase we are zooming the center image view parent
if (self.centerImgView.superview == scrollView){
return self.centerImgView;
}

return nil;
}
---

Double tap gesture and zoom:

The method “- (void)viewDidLoad”, we add a gesture recogniser to the hostScrollView.
--
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
recognizer.numberOfTapsRequired = 2;
recognizer.delegate = self;
[self.imageHostScrollView addGestureRecognizer:recognizer];
---

And the handler will set the required scale values.

- (void)handleTap:(UITapGestureRecognizer *)recognizer {
UIScrollView *scrollView = (UIScrollView*)self.centerImgView.superview;
float scale = scrollView.zoomScale;
scale += 1.0;
if(scale > 2.0) scale = 1.0;
[scrollView setZoomScale:scale animated:YES];
}

Conclusion:

This article explains on how to build a gallery app.
Download and go through the source code, explore it, and let us know your valuable comments.
Also for any clarifications please post a comment.

Find the source code below:
Source Code

See you in the next blog post!!!