views:

1595

answers:

5

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

+3  A: 

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.)
Tim
Here's a link http://iphonedevelopment.blogspot.com/2009/02/kvo-and-iphone-sdk.html explaining KVO for anyone who doesn't know what that is. Like I didn't.
Grumdrig
Grumdrig
But why bother changing the content offset at all? Let it scroll, just make sure you keep up and move the image views to correspond to what the user's seeing.
quixoto
A: 

Were you able to do the infinite scrolling?

wolverine
Problem is, it takes forever.
Elise van Looij
A: 

Perhaps setting contentSize to some gigantic value and then moving a limited number of underlying views around to track the view position as in the Tiling sample will do the trick.

To mitigate the possibility of eventually reaching an edge and having to abruptly recenter the view (which cancels any scrolling currently in motion), the view can be recentered when it is stationary, from time to time.

Anyway, that's what I'm about to try.

Grumdrig
This seems to work fine, FYI. I'm using 4 contained views the same size as the containing view that march along with the scrolling. I've based my code on the (upper half of the) Scrolling sample.
Grumdrig
+3  A: 

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.

Allisone
A: 

Bear in mind that when the scroll is animated, contentOffset changes many times, not just page by page, but with each step in the animation.