UIScrollView. Any thoughts on implementing "infinite" scroll/zoom?

So, UITableView supports essentially "infinite" scrolling. There' may be a limit but that sucker can scroll for a looonnnggg time. I would like to mimic this behavior with a UIScrollView but there are two fundamental impediments:

1) scrollView.contentSize is fixed at creation time. 2) zooming can blow any lazy-loading scheme all to hell since it can cause infinte data explosion.

Have others out there pondered this idea? Yah, I know, we are essentially talking about re-creating Google Maps here. Any insights would be much appreciated.

Cheers, Doug


I've just finished implementing the infitine scroll for me. In my Implementation I have UITableViewCell with a scrollView and Navigationbuttons. The scrollView contains x views all with the same width. views are alined horizontally and paging is enabled.

scrollView.clipsToBounds = YES;
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsHorizontalScrollIndicator = NO;

My codelogic is like the following:

  1. In my initialization function I
    • create all the views (for the scrollview) and
    • put them into an array and
    • add them to the scrollView
  2. Then I call a function that calculates in a loop the positions for each view (each time you detect a scroll this function will need to be called too). It always takes the first element of the array and sets the frame to (0,0,...,...), the second with (i*width,0,....,....) and so on. The function beeing called looks like this:

    - (void)updateOffsetsOfViews{
        int xpos = 0;
        for (int i=0; i<[views count]; i++) {
            UIImageView *_view = [views objectAtIndex:i];
            CGRect aFrame = _view.frame;
            aFrame.origin.x = xpos;
            aFrame.origin.y = 0.0;
            _view.frame = aFrame;
            xpos += viewWidth;
        }
        float center = 0;
        if(fmod([views count],2) == 1){
            center = viewWidth * ([views count]-1)/2;
        }else {
            center = viewWidth * [views count]/2;
        }
        [scrollView setContentOffset:CGPointMake(center, 0)];
        lastOffset = center;
    }
    
  3. Then (still in the initialization process) I add an observer

    [scrollView addObserver:self forKeyPath:@"contentOffset" options:0 context:nil];
    

    so each time something in the scrollView changes I get the (observeValueForKeyPath)-function called, which looks like this:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object  change:(NSDictionary *)change context:(void *)context
    {
        UIImageView *_viewFirst = (UIImageView *)[views objectAtIndex:0];
        if ( fmod([scrollView contentOffset].x,viewWidth)  == 0.0) {
            if ([scrollView contentOffset].x > lastOffset) {
                [views removeObjectAtIndex:0];
                [views addObject:_viewFirst];                
                [self updateOffsetsOfViews];    
            }else if ([scrollView contentOffset].x < lastOffset) {
                UIImageView *_viewLast = (UIImageView *)[views lastObject];
                [views removeLastObject];
                [views insertObject:_viewLast atIndex:0];            
                [self updateOffsetsOfViews];
            }
        }
    }
    
  4. And in dealloc or viewDidUnload (depends on how you implement it) don't forget to remove the observer.

    [scrollView removeObserver:self forKeyPath:@"contentOffset"];   
    

Hope this helps, you might notice some overhead, but in my implementation I also support like scrolling 5 pages (well... unlimited) at once and autoanimated scrolling etc. so you might see something that could be thrown away.


While it's impossible to have a truly infinite UIScrollView, there are some simple tricks you can use to emulate that behavior.

  1. Handling the fixed contentSize: have some fixed-size view handled by your scroll view, and at launch or instantiation, set the content offset so that you're seeing the middle of the handled view. Then just watch the content offset (using KVO or some other method), and if you near any edge, update the content of the view with a new set of content (offset appropriately) and reset the scroll view's contentOffset property to be back in the middle.
  2. Handling zooming: do something similar, only this time watch the zoom factor on the scroll view. Whenever it gets to a certain point, do some manipulation to whatever data you're presenting so that it appears zoomed, then reset the zoom factor to 1.0. For example, if you're scrolling an image and it gets zoomed to appear twice as large, programmatically apply some kind of transform to make the image twice as large, then reset the scroll view's zoom factor to 1.0. The image will still appear zoomed in, but the scroll view will be able to continue zooming in further as necessary. (Google Maps takes this one step further where it lazy-loads more detailed views as the user zooms - you may or may not choose to implement this.)

The StreetScroller sample project from Apple demonstrates how to perform infinite scrolling in a UIScrollView.