Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Yay! I tried to approach the problem with just one UIScrollView, and I think I found a solution.</p> <p>Before the user starts zooming (in <code>viewForZoomingInScrollView:</code>), I switch the scroll view into zooming mode (remove all additional pages, reset content size and offset). When the user zooms out to scale 1.00 (in <code>scrollViewDidEndZooming:withView:atScale:</code>), I switch back to paging view (add all pages back, adjust content size and offset).</p> <p>Here's the code of a simple view controller that does just that. This sample switches between, zooms and pans three large UIImageViews.</p> <p>Note that a single view controller with a handful of functions is all it takes, no need to subclass UIScrollView or something.</p> <pre><code>typedef enum { ScrollViewModeNotInitialized, // view has just been loaded ScrollViewModePaging, // fully zoomed out, swiping enabled ScrollViewModeZooming, // zoomed in, panning enabled ScrollViewModeAnimatingFullZoomOut, // fully zoomed out, animations not yet finished ScrollViewModeInTransition, // during the call to setPagingMode to ignore scrollViewDidScroll events } ScrollViewMode; @interface ScrollingMadnessViewController : UIViewController &lt;UIScrollViewDelegate&gt; { UIScrollView *scrollView; NSArray *pageViews; NSUInteger currentPage; ScrollViewMode scrollViewMode; } @end @implementation ScrollingMadnessViewController - (void)setPagingMode { NSLog(@"setPagingMode"); if (scrollViewMode != ScrollViewModeAnimatingFullZoomOut &amp;&amp; scrollViewMode != ScrollViewModeNotInitialized) return; // setPagingMode is called after a delay, so something might have changed since it was scheduled scrollViewMode = ScrollViewModeInTransition; // to ignore scrollViewDidScroll when setting contentOffset // reposition pages side by side, add them back to the view CGSize pageSize = scrollView.frame.size; NSUInteger page = 0; for (UIView *view in pageViews) { if (!view.superview) [scrollView addSubview:view]; view.frame = CGRectMake(pageSize.width * page++, 0, pageSize.width, pageSize.height); } scrollView.pagingEnabled = YES; scrollView.showsVerticalScrollIndicator = scrollView.showsHorizontalScrollIndicator = NO; scrollView.contentSize = CGSizeMake(pageSize.width * [pageViews count], pageSize.height); scrollView.contentOffset = CGPointMake(pageSize.width * currentPage, 0); scrollViewMode = ScrollViewModePaging; } - (void)setZoomingMode { NSLog(@"setZoomingMode"); scrollViewMode = ScrollViewModeInTransition; // to ignore scrollViewDidScroll when setting contentOffset CGSize pageSize = scrollView.frame.size; // hide all pages besides the current one NSUInteger page = 0; for (UIView *view in pageViews) if (currentPage != page++) [view removeFromSuperview]; // move the current page to (0, 0), as if no other pages ever existed [[pageViews objectAtIndex:currentPage] setFrame:CGRectMake(0, 0, pageSize.width, pageSize.height)]; scrollView.pagingEnabled = NO; scrollView.showsVerticalScrollIndicator = scrollView.showsHorizontalScrollIndicator = YES; scrollView.contentSize = pageSize; scrollView.contentOffset = CGPointZero; scrollViewMode = ScrollViewModeZooming; } - (void)loadView { CGRect frame = [UIScreen mainScreen].applicationFrame; scrollView = [[UIScrollView alloc] initWithFrame:frame]; scrollView.delegate = self; scrollView.maximumZoomScale = 2.0f; scrollView.minimumZoomScale = 1.0f; UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"red.png"]]; UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"green.png"]]; UIImageView *imageView3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"yellow-blue.png"]]; // in a real app, you most likely want to have an array of view controllers, not views; // also should be instantiating those views and view controllers lazily pageViews = [[NSArray alloc] initWithObjects:imageView1, imageView2, imageView3, nil]; self.view = scrollView; } - (void)setCurrentPage:(NSUInteger)page { if (page == currentPage) return; currentPage = page; // in a real app, this would be a good place to instantiate more view controllers -- see SDK examples } - (void)viewDidLoad { scrollViewMode = ScrollViewModeNotInitialized; [self setPagingMode]; } - (void)viewDidUnload { [pageViews release]; // need to release all page views here; our array is created in loadView, so just releasing it pageViews = nil; } - (void)scrollViewDidScroll:(UIScrollView *)aScrollView { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(setPagingMode) object:nil]; CGPoint offset = scrollView.contentOffset; NSLog(@"scrollViewDidScroll: (%f, %f)", offset.x, offset.y); if (scrollViewMode == ScrollViewModeAnimatingFullZoomOut &amp;&amp; ABS(offset.x) &lt; 1e-5 &amp;&amp; ABS(offset.y) &lt; 1e-5) // bouncing is still possible (and actually happened for me), so wait a bit more to be sure [self performSelector:@selector(setPagingMode) withObject:nil afterDelay:0.1]; else if (scrollViewMode == ScrollViewModePaging) [self setCurrentPage:roundf(scrollView.contentOffset.x / scrollView.frame.size.width)]; } - (UIView *)viewForZoomingInScrollView:(UIScrollView *)aScrollView { if (scrollViewMode != ScrollViewModeZooming) [self setZoomingMode]; return [pageViews objectAtIndex:currentPage]; } - (void)scrollViewDidEndZooming:(UIScrollView *)aScrollView withView:(UIView *)view atScale:(float)scale { NSLog(@"scrollViewDidEndZooming: scale = %f", scale); if (fabsf(scale - 1.0) &lt; 1e-5) { if (scrollView.zoomBouncing) NSLog(@"scrollViewDidEndZooming, but zoomBouncing is still true!"); // cannot call setPagingMode now because scrollView will bounce after a call to this method, resetting contentOffset to (0, 0) scrollViewMode = ScrollViewModeAnimatingFullZoomOut; // however sometimes bouncing will not take place [self performSelector:@selector(setPagingMode) withObject:nil afterDelay:0.2]; } } @end </code></pre> <p>Runnable sample project is available at <a href="http://github.com/andreyvit/ScrollingMadness/" rel="nofollow noreferrer">http://github.com/andreyvit/ScrollingMadness/</a> (if you don't use Git, just click Download button there). A README is available there, explaining why the code was written the way it is.</p> <p>(The sample project also illustrates how to zoom a scroll view programmatically, and has ZoomScrollView class that encapsulates a solution to that. It is a neat class, but is not required for this trick. If you want an example that does not use ZoomScrollView, go back a few commits in commit history.)</p> <p>P.S. For the sake of completeness, there's TTScrollView — UIScrollView reimplemented from scratch. It's part of the great and famous <a href="http://github.com/joehewitt/three20/" rel="nofollow noreferrer">Three20 library</a>. I don't like how it feels to the user, but it does make implementing paging/scrolling/zooming dead simple.</p> <p>P.P.S. The real Photo app by Apple has pre-SDK code and uses pre-SDK classes. One can spot two classes derived from pre-SDK variant of UIScrollView inside PhotoLibrary framework, however it is not immediately clear what they do (and they do quite a lot). I can easily believe this effect used to be harder to achieve in pre-SDK times.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload