Scrolling the Scroll View Content

The most common method of initiating the scrolling of a scroll view is a direct manipulation by the user touching the screen and dragging with his or her finger. The scroll content then scrolls in response to the action. This gesture is referred to as a drag gesture.

A variation of the drag gesture is the flick gesture. A flick gesture is a quick movement of a user’s finger that makes initial contact with the screen, drags in the direction of the desired scroll, and then lifts from the screen. This gesture not only causes scrolling, it imparts a momentum, based on the speed of the user’s dragging action, that causes scrolling to continue even after the gesture is completed. The scrolling then decelerates over a specified period of time. The flick gesture allows users to move large distances with a single action. At any time during the deceleration, the user can touch the screen to stop the scrolling in place. All this behavior is built into UIScrollView and requires no implementation on the part of the developer.

But sometimes it’s necessary for the application to scroll content programmatically, for example, to display a specific portion of a document. In those cases, UIScrollView provides the required methods.

The UIScrollView delegate protocol UIScrollViewDelegate provides methods that allow your application to track the scrolling progress and respond as appropriate to the specific needs of your application.

Scrolling Programmatically

Scrolling the content of a scroll view is not always in response to the user’s finger dragging or flicking the screen. There are times when your application will need to scroll to a certain content offset, so that a specific rectangular area is exposed, or to the top of the scroll view. UIScrollView provides methods to perform all these actions.

Scrolling to a Specific Offset

Scrolling to a specific top-left location (the contentOffset property) can be accomplished in two ways. The setContentOffset:animated: method scrolls the content to the specified content offset. If the animated parameter is YES, the scrolling will animate from the current position to the specified position at a constant rate. If the animated parameter is NO, the scrolling is immediate and no animation takes place. In both cases, the delegate is sent a scrollViewDidScroll: message. If animation is disabled, or if you set the content offset by setting the contentOffset property directly, the delegate receives a single scrollViewDidScroll: message. If animation is enabled, then the delegate receives a series of scrollViewDidScroll: messages as the animation is in progress. When the animation is complete, the delegate receives a scrollViewDidEndScrollingAnimation: message.

Making a rectangle visible

It is also possible to scroll a rectangular area so that it is visible. This is especially useful when an application needs to display a control that is currently outside the visible area into the visible view. The scrollRectToVisible:animated: method scrolls the specified rectangle so that it is just visible inside the scroll view. If the animated parameter is YES, the rectangle is scrolled into view at a constant pace. As with setContentOffset:animated:, if animation is disabled, the delegate is sent a single scrollViewDidScroll: message. If animation is enabled, the delegate is sent a series of scrollViewDidScroll: messages as animation progresses. In the case of scrollRectToVisible:animated: the scroll view’s tracking and dragging properties are also NO.

If animation is enabled for scrollRectToVisible:animated:, the delegate receives a scrollViewDidEndScrollingAnimation: message, providing notification that the scroll view has arrived at the specified location and animation is complete.

Scroll To Top

If the status bar is visible a scroll view can also scroll to the top of the content in response to a tap on the status bar. This practice is common in applications that provide a vertical representation of data. For example, the Photos application supports scroll to top, both in the album selection table view and when viewing thumbnails of the photos in an album and most UITableView implementations (a subclass of UIScrollView) also support scroll to top.

Your application enables this behavior by implementing the delegate method the scroll view property scrollViewShouldScrollToTop: and return YES. This delegate method allows fine-grained control over which scroll view will scroll to the top if there are multiple scroll views on the screen at one time by returning the scroll view to scroll.

When scrolling is complete, the delegate is sent a scrollViewDidScrollToTop: message, specifying the scroll view.

Delegate Messages Sent During Scrolling

As scrolling occurs, the scroll view tracks state using the tracking, dragging, decelerating, and zooming properties. In addition, the contentOffset property defines the point in the content that is visible at the top left of the scroll view’s bounds. The following table describes each of the state properties:

State property

Description

tracking

YES if the user’s finger is in contact with the device screen.

dragging

YES if the user’s finger is in contact with the device screen and has moved.

decelerating

YES if the scroll view is decelerating as a result of a flick gesture, or a bounce from dragging beyond the scroll view frame.

zooming

YES if the scroll view is tracking a pinch gesture to change its zoomScale property.

contentOffset

A CGPoint value that defines the top-left corner of the scroll view bounds.

It isn’t necessary to poll these properties to determine the action in progress because the scroll view sends a detailed sequence of messages to the delegate, indicating the progress of the scrolling action. These methods allow your application to respond as necessary. The delegate methods can query those state properties to determine why the message was received or where the scroll view currently is.

The Simple Approach: Tracking The Start and Completion Of A Scroll Action

If your application is interested only in the beginning and ending of the scrolling process, you can implement only a small subset of the possible delegate methods.

Implement the scrollViewWillBeginDragging: method to receive notification that dragging will begin.

To determine when scrolling is complete you must implement two delegate methods: scrollViewDidEndDragging:willDecelerate: and scrollViewDidEndDecelerating:. Scrolling is completed either when the delegate receives the scrollViewDidEndDragging:willDecelerate: message with NO as the decelerate parameter, or when your delegate receives the scrollViewDidEndDecelerating: method. In either case, scrolling is complete.

The Complete Delegate Message Sequence

When the user touches the screen the tracking sequence begins. The tracking property is set to YES immediately, and remains YES as long as the user’s finger is in contact with the screen, regardless of whether they are moving their finger.

If the user’s finger remains stationary and the content view responds to touch events, it should handle the touch, and the sequence is complete.

However, if the user moves the finger, the sequence continues.

When the user begins to move his or her finger to initiate scrolling the scroll view first attempts (assuming the default values of the scroll view) to cancel any in progress touch handling, if it is attempting to do so.

The scroll view’s dragging property is set to YES, and its delegate is sent the scrollViewWillBeginDragging: message.

As the user drags his or her finger, the scrollViewDidScroll: message is sent to the delegate. This message is sent continuously as scrolling continues. Your implementation of this method can query the scroll view’s contentOffset property to determine the location of the top-left corner of the scroll view bounds. The contentOffset property is always the current location of the top-left corner of the scroll bounds, whether scrolling is in progress or not.

If the user performs a flick gesture, the tracking property is set to NO, because in order to perform a flick gesture, the user’s finger breaks contact with the screen after the initial gesture begins the scroll. At this time the delegate receives a scrollViewDidEndDragging:willDecelerate: message. The deceleration parameter will be YES as the scrolling decelerates. The deceleration speed is controlled by the the decelerationRate property. By default, this property is set to UIScrollViewDecelerationRateNormal, which allows scrolling to continue for a considerable time. You can set the rate to UIScrollViewDecelerationFast to cause the deceleration to take significantly less time, the scrolled distance after the flick gesture to be much shorter. As a view decelerates, the decelerating property of the scroll view is YES.

If the user drags, stops dragging and then lifts their finger from the screen the delegate receives the scrollViewDidEndDragging:willDecelerate: message, however the deceleration parameter is NO. This is because no momentum has been imparted on the scroll view. Because the user’s finger is no longer in contact with the screen the tracking property value is NO.

If the decelerate parameter of the scrollViewDidEndDragging:willDecelerate: message is NO, then the scroll view delegate will receive no more delegate messages for this drag action. The scroll view decelerating property also now returns a value of NO.

There is one other condition that causes the scrollViewDidEndDragging:willDecelerate: message to be sent to the delegate, even if the user has lifted his or her finger when is stationary. If the scroll view is configured to provide the visual cue of bouncing when the user drags the content past the edge of the scrolling area, the scrollViewDidEndDragging:willDecelerate: message is sent to the delegate with YES as the decelerate parameter. Bouncing is enabled when the bounces property is YES (the default state). The alwaysBounceVertical and alwaysBounceHorizontal properties do not affect the behavior of the scroll view when bounces is NO. If bounces is YES, they allow bouncing when the contentSize property value is smaller than the scroll view bounds.

Regardless of the condition that caused the scroll view to receive the scrollViewDidEndDragging:willDecelerate: message, if the decelerate parameter is YES, the scroll view is sent the scrollViewWillBeginDecelerating: message. During deceleration, the delegate continues to receive the scrollViewDidScroll: message, although the tracking and dragging values are now both NO. The decelerating property continues to be YES.

Finally, when the scroll view deceleration completes, the delegate is sent a scrollViewDidEndDecelerating: message, the decelerating property has a value of NO, and the scrolling sequence is complete.