Interacting with UIScrollView

UIScrollView handles two types of gestures: pinch for zooming and drag for scrolling the content. The UIGestureRecognizers related to these two gestures are exposed as properties of the scroll view object (panGestureRecognizer and pinchGestureRecognizer).

Before reading on, you can download here the Xcode project for this article.

Zooming in a UIScrollView

The UIScrollView zooming feature is enabled by setting at least one of the minimumZoomScale or maximumZoomScale properties to a value different from 1.0 (which is their default). Usually the maximumZoomScale is greater than 1.0 and allows zooming in, while minimumZoomScale is less or equal to 1.0 and allows zooming out.

We also have to specify the scroll view’s subview to be zoomed when the pinch gesture is detected. Normally we want all the content to be zoomed. If multiple subviews form the content, we should create a container view the size of the contentSize which would be the main subview of the scroll view. This container view encloses all the other subviews and its reference should be returned by the viewForZoomingInScrollView: delegate method.

container view for zooming

When the zoom scale is less than 1.0 (allowed by minimumZoomScale between 0.0 and 1.0), the container view size could become smaller than the scroll view bounds size; in this case, the container view is pinned to the top left corner of the scroll view. This behavior can be changed by subclassing UIScrollView and overriding the layoutSubviews method.

There are two ways to perform zooming programmatically: – using the zoomToRect:animated: method, which automatically scrolls and zooms to the rectangle area set in the first argument, adjusting the zoom scale so the rectangle occupies as much screen space as possible – using the setZoomScale:animated: method, which modifies the zoom scale keeping the center of the visible area in the center of the screen.

Scrolling using paging mode

By default, UIScrollView uses continuous scroll, but it’s easy to add paging mode and scroll the content by fractions equal to the scroll view bounds size.

Enabling paging mode in a scroll view is as simple as setting the pagingEnabled property to YES.

The behavior is very similar to using a UIPageViewController, but it offers a bit more flexibility if we wanted to control precisely what happens when the user scrolls, thanks to a larger number of delegate methods.

Adding custom gesture recognizers

In addition to the default gesture recognizers of the scroll view (pan and pinch), it is possible to handle other types of gestures and enable different interactions with the scroll view content.

As mentioned at the beginning of the article, the UIScrollView has two gestures recognizers attached by default, an UIPanGestureRecognizer for scrolling and a UIPinchGestureRecognizer for zooming, and these gesture recognizers are exposed as properties of the scroll view. In fact, there is a third gesture recognizer, which is private. Its purpose is to delay touches recognition on the scroll view by a fraction of a second, leaving the scroll view enough time to figure out what kind of gesture is in progress. The delay gesture recognizer basically cancels all the gestures for a very short time.

Double-tap to zoom

If we want to implement special behaviors for the single tap gesture (e.g. select a subview inside the scroll view) and the double taps (e.g. zooming the scroll view), because there isn’t a UIDoubleTapGestureRecognizer class, we have to use the UITapGestureRecognizer to detect single and double taps.

To prevent the system to recognize any of the two taps from a double-tap as a sigle tap gesture, we call the requireGestureRecognizerToFail: on the single tap gesture recognizer.

In the next code sample, the resetZoomScaleOnDoubleTap method changes the zoom scale to its default value of 1 if the scroll view is zoomed in or out:

Dragging a subview inside a scroll view

Changing the position of a subview inside the scroll view is equivalent to selecting it with a long-press gesture recognizer, dragging it on the screen to the final position and dropping it there.

To handle all these actions, we use a UILongPressGestureRecognizer attached to the subview, and not to the scroll view. When the subview is dragged across the screen, we actually change its center property to the current coordinate of the finger.

Here is the code snippet performing the drag & drop on the long-press gesture. Note that I didn’t implement the automatic scrolling when the dragged subview partially reaches outside the bounds of the scroll view.

Conclusion

UIScrollView is a very versatile class, but customizing its already rich default behavior with additional gesture recognizers is pretty easy to do if we want to enhance the user experience.

 

Catalin Rosioru