Tutorial: All About Images (Part 2) — Panning & Zooming with UIScrollView

In this tutorial I will explain how to embed a UIImageView in a UIScrollView to allow zooming and panning of images, similar to Apple’s Photo app. I’ll dig a little bit deeper than most tutorials on the web and explain how to work around some of UIScrollView's odd behavior.

Getting Started

So much time has passed since I wrote first part of this tutorial that some changes need to be made to the completed source code from that step before continuing. Specifically, I have decided to use Automatic Reference Counting (ARC) to simplify memory management. I’ll talk more about ARC in an upcoming tutorial. So to get started download the update source code AllAbountImages2ARC_StartingPoint

When you open the project you will also notice that I have provided a ready-made .xib file for the SecondViewController class. This .xib consists of a UIScrollView that contains a UIImageView. These views are already connected to the scrollView and imageView IBOutlets in the SecondViewController class. The project should compile and run for you as downloaded, but when you press the tab for the second view controller all you get is a white screen.

Panning in a UIScrollView

To enable panning in a UIScrollView all you need to do is tell the scroll view the size of the content and, of course, give it something more interesting that an empty UIImageView to pan! To do this, open the file SecondViewController.m and modify your viewDidLoad method as shown below.

Figure 1: Panning in UIScrollView (note scroll indicator below image).

If all goes well, you should be able to run your project, hit the “Second” tab and be presented with an image of Kinkaku-ji that you can pan. As you pan the UIScrollView will display scroll indicators as shown in Figure 1.

Try commenting out line 14 in the code above. Notice that you will not be able to pan the image. So if you are using a UIScrollView and it refuses to pan, it probably means you forgot to set the contentSize property!

So is that all there is to panning? Unfortunately, not quite, as we will see later in the tutorial under certain circumstances the default behavior of UIScrollView is probably not what we expect. More about that later.

Zooming in a UIScrollView

To get zooming to work in a UIScrollView there are a few requirements:

  • the maximumScale property must be set,
  • the delegate property must be set, and
  • the delegate must implement the viewForZoomingInScrollView: selector.

Open the SecondViewController.h file and add a UIScollViewDelegate protocol specification to the interface line as shown.

This line tells the compiler that you are going to implement some of the UIScrollViewDelegate methods and make your view controller eligible to be assigned to the delegate property of a UIScrollView.

Next, open the file SecondViewController.m and the following method at the end of the file.

When the scroll view detects a pinch gesture for zooming it calls this method to get a suitable view to use for zooming. In our case, this is straightforward, we just use our image view but if, for example, our content had complex vector graphics that took some time to redraw we might return a special simplified view from this method.

Finally, modify your viewDidLoad method by adding the highlighted lines.

Now if you compile and run your project you should be able to both zoom and pan. If you are working on the simulator you can use option-drag to simulate pinch gestures.

Working Around UIScrollView's Strange Behavior

When you run the tutorial, if you look very carefully, you may notice that the photo is not exactly vertically centered in the UIScrollView. Since the example image is almost the same height as the scroll view this may be hard to see. To make it more obvious change the line that loads the image to load the smaller GoldenGate.png image.

Now rerun the tutorial. The image will appear somewhere in the top right of the scroll view and zooming will behave strangely. To be quite honest, I am not sure how Apple intended this to behave, but it certainly seems odd. The real issue seems to be that UIScrollView does not like the frame origins of any of its subviews to be negative or for the contentOffset to be negative. To work around this you will add a method to place a UIView by center in a UIScrollView.

First add the method definition to SecondViewController.h:

Now add the method implementation to SecondViewController.m

Let’s take a closer look at what this method does. Lines 3 and 4 make local copies of the view’s frame and the scroll view’s contentOffset. Lines 6 and 7 calculate the x and y coordinates of the view’s origin from the centerPoint parameter. If the calculated x is negative then in lines 11 and 12 the x coordinate of our copy of the view’s frame origin is set to 0 and we set our copy of the scroll view’s contentOffset to shift the view appropriately. If the calculated value is positive we can use it as the frame origin. Lines 18-26 carry out the same logic for the y coordinate. Finally, lines 28 and 29 set the modified values in the view and scroll view.

To use this code override viewDidAppear: in SecondViewController.m

Now compile and run the project and the small Golden Gate image should appear in the center of view.

So is that it? Unfortunately, no! Try zooming the image and notice that it behaves strangely.

It turns out that, left to its own devices, a UIScrollView will (sometimes) set the frame origin of the zoom view to a negative value and confuse itself! The way to work around this is to use the scrollViewDidZoom: delegate method to ensure this does not happen.

Add the following code to SecondViewController.m:

Let’s examine this code more carefully. Lines 3 and 4 get the zoom view and a copy of its frame. In lines 5-12, if the zoom view, at the current zoom level, is narrower than the scroll view then the frame origin is set to ensure the zoom view is centered horizontally within the scroll view otherwise the origin is set to zero. Lines 13-20 carry out the same logic in the vertical direction.

If you now compile and run your project the zooming and panning behavior should correct.

You can download the completed code for this tutorial here: AllAbountImages2ARC_EndPoint

This site is ad supported. Please consider visiting our sponsors while downloading.

About idz

A professional software engineer dabbling in iOS app development.
This entry was posted in Tutorial and tagged , , , . Bookmark the permalink.

2 Responses to Tutorial: All About Images (Part 2) — Panning & Zooming with UIScrollView

  1. Pingback: Subby

  2. julelee says:

    Thank you so much for this great tutorial.
    I’d like to use multiple images by using this codes.
    However, imageNamed code is letting me only use on image file even though I tried to apply the view controller to several other images.
    How can I use this view to another images in one project?
    I’m using storyboard, and I’m trying to make 5 views that has this zoom funtion using by the same view controller. Please advise me. Thanks!

Leave a Reply