Here's how I implemented the intro sequence:
As UIScrollViewDelegate didn't provide me the actual displayed coordinates of the scrollview content, I created a new class derived from UIScrollView and overrode setContentOffset. I also defined a delegate protocol which is triggered when setContentOffset is called and informs listeners of the contentOffset value passed into setContentOffset method. (Ideally this would have been part of the iOS's UIScrollViewDelegate delegate).
The implementer of the delegate is now able to receive the actual content offset as scrolling occurs. As the view is scrolled and different content is displayed, I trigger animations based on the vertical coordinate of the contentOffset. These triggers are defined in an array of vertical points ( Y values ) each associated with two function calls which animation UI elements in and out of view.
When the contentOffset Y value is greater than a trigger point, I animated a specific UI element into view and set the animation as "triggered". The animation is typically a fade in or resize using Block-Based Animation. When the Y value is less than the trigger point and the animation has previously be triggered, I animate the object out of view using a different animation, such as fade-out, and mark is as "untriggered"
Here's the key computation to determine if a UI element is visible:
CGPoint point = [contentOffset CGPointValue];
CGFloat visible = _scrollView.bounds.size.height + point.y;
if ( _triggers[ndx].y < visible )
{
if ( !_triggers[ndx].triggered )
{
_triggers[ndx].triggered = true;
// call trigger's function to animate item into view
}
}
else
{
if (_triggers[ndx].triggered)
{
_triggers[ndx].triggered = false;
// call trigger's function to animate item out of view
}
}