views:

9391

answers:

5

OK, I think it's time to make an official place on the internet for this problem: How to make a UIScrollView photoviewer with paging and zooming. Welcome my fellow UIScrollView hackers.

I have a UIScrollView with paging enabled, and I'm displaying UIImageViews like the built-in photos app. (Does this sound familiar yet?)

I found the following project on github:

http://wiki.github.com/andreyvit/ScrollingMadness

Which shows how to implement zooming in a scroll view while paging is enabled. If anyone else tries this out, I actually had to remove the UIScrollView subclass and use the native class otherwise it doesn't work. I think it's because of changes in the 3.0 SDK relating to how the scroll view intercepts touch events.

So the the idea is to remove all the other views when you start zooming, and move the current view to (0, 0) in the scrollview, updating the contentsize etc. Then when you zoom back to 1.0f it adds the other views back and puts things all back in order.

Anyway, that project works perfectly in the simulator, but on the device there is some nasty movement of the view you are resizing, which looks like it's caused by the fact we are changing the contentsize/offset etc. for the view being resized. You have to do this view moving otherwise you can pan left through the whitespace left by the other views.

I found one interesting note in the "Known Issues" of the 3.0 SDK release notes:

UIScrollView: After zooming, content inset is ignored and content is left in the wrong position.

This kind of sounds like what is happening here. After zooming in, the view will shift offscreen because you have changed the offset etc.

I've spent hours on this already and I'm slowing coming to the sad realization that this just isn't going to work.

Three20's photo viewer is out of the question: it's too heavy weight and there is too much unnecessary UI and other behaviour.

The built in Photo app seems to do some magic. If you zoom in on an image and pan to the far edges, the current photo moves independently of the photo next to it which isn't what you get when trying this with a standard UIScrollView.

I've seen discussion about nesting the UIScrollView's but I really don't want to go there.

Has anybody managed this with the standard UIScrollView (and works in the 2.2 and 3.0 SDK)? I don't fancy rolling my own zoom + bounce + pan + paging code.

A: 

Take a look at http://github.com/joehewitt/three20/blob/master/src/Three20/TTPhotoViewController.h Not sure if that's what you are looking for

Rafael Vega
Like I said in the OP, Three20's photo viewer is out of the question.
Mike Weller
+2  A: 

I did some playing around with the native Photos app, and I think I can say with confidence they are using a single UIScrollView. The giveaway is this: zoom in on an image, and pull to the left or right. You will see the next or previous photo. If you tug hard enough, it will even page to the next photo at 1.0f zoom. Flip back and the previously zoomed photo will be back to 1.0f zoom as well.

Obivously I didn't write Photos.app, but I'll take a wild guess at how they did it:

  • A single UIScrollView and a single UIScrollViewDelegate
  • Populate the UIScrollView with UIImageView children
  • Listen for scrollViewDidScroll:
  • Do some math and figure out what page you are currently on
  • Listen for viewForZoomingInScrollView:
  • Return a different view depending on the page index
  • Listen for scrollViewDidEndZooming:withView:atScale: and optionally do some anti-aliasing, etc based on the content

If you decide to try that out, let me know how it works out for you. I'd love to know how you finally end up getting this to work. Even better, post it to github.

slf
A: 

I did some playing around with the native Photos app, and I think I can say with confidence they are using a single UIScrollView. The giveaway is this: zoom in on an image, and pull to the left or right. You will see the next or previous photo. If you tug hard enough, it will even page to the next photo at 1.0f zoom. Flip back and the previously zoomed photo will be back to 1.0f zoom as well.

This is wrong. I'm using nested scrollviews, and getting exactly the same effect. If you're using some memory management scheme (which I had to start using... my page number is fairly high ('bout 50 each in 2 scrollViews)), then you can use a mechanism similar to whatever you have triggering your page loads / unloads to trigger a zoom reset for the pages -1 and +1 from the current page.

I suspect that apple sets this off as soon as the previous pic has disappeared.

What I don't understand is how to achieve smooth scrolling between pages - there's always a very short hang at the moment of transition. Do not get it. I've gotten pretty deep into fixing it - NSInvocationOperations were my first stop, then I made a reusable views queue for the page views (which retain their image views)... still this durned hang.

I only have one NSOperationQueue running, and I've tried fiddling with the max number of concurrent operations. My thought was that the main thread was getting clogged by competing Queues, or maybe even one queue trying to do to much... still, the hang.

I even tried creating super low-qual versions of my media, in case that was the problem. With each image weighing in at around 10k (these are jpegs, mind you)... you guessed it. The hang's still there.

I'm pretty much resolved to do what I've done before and use TTPhotoViewController from Three20. I've spent some hours swimming through that code, and it's always a great education. At this point, though, I would really like to know where the heck this hang comes from, if only so I can spend my can't-sleep hours wondering about something less brain boiling.

beOn
Have you tried a CATiledLayer?
drawnonward
Nope... have you?
beOn
A: 

sure would be nice if apple built an image viewer like the photos app into the SDK for us to use. I'm currently using three20 and it works great. But it is a lot of extra stuff to carry around when all you really want is the photo viewer.

Francois
+10  A: 
Jonah
For those that don't want to hunt through all of the sessions and sample code, you're looking for session 104, Designing Apps with Scroll Views.
Steve Madsen
I just finished watching the session video and looking through the sample code, and it's really good stuff. My one complaint: the ultimate goal of using `CATiledLayer` for great performance and low memory use requires pre-processing images into tiles. At best, this can be extremely inconvenient.
Steve Madsen
I agree. Clearly they found a way around this for the Photos app. It either tiles the images on the fly or has some other way for handling large images.
Jonah
Note that cutting images into tiles and naming them all right for a give zoom level is just a one-line command with ImageMagick: http://stackoverflow.com/questions/3245351/iphone-tiling-an-image
foz
Yes, they expect that each image will be one image, perhaps very high resolution, and they use CATiledLayer and generate the tiles inside some drawing code.
Paul Shapiro