Alex Rivera | Logout

How do I reset after a UIScrollView zoom?

Asked 2009-01-15T20:16:25.637
21

I have a Graph being drawn inside a UIScrollView. It's one large UIView using a custom subclass of CATiledLayer as its layer.

When I zoom in and out of the UIScrollView, I want the graph to resize dynamically like it does when I return the graph from viewForZoomingInScrollView. However, the Graph redraws itself at the new zoom level, and I want to reset the transform scale to 1x1 so that the next time the user zooms, the transform starts from the current view. If I reset the transform to Identity in scrollViewDidEndZooming, it works in the simulator, but throws an EXC_BAD_ACCSES on the device.

This doesn't even solve the issue entirely on the simulator either, because the next time the user zooms, the transform resets itself to whatever zoom level it was at, and so it looks like, if I was zoomed to 2x, for example, it's suddenly at 4x. When I finish the zoom, it ends up at the correct scale, but the actual act of zooming looks bad.

So first: how do I allow the graph to redraw itself at the standard scale of 1x1 after zooming, and how do I have a smooth zoom throughout?

Edit: New findings The error seems to be "[CALayer retainCount]: message sent to deallocated instance"

I'm never deallocating any layers myself. Before, I wasn't even deleting any views or anything. This error was being thrown on zoom and also on rotate. If I delete the object before rotation and re-add it afterward, it doesn't throw the exception. This is not an option for zooming.

Edit
Report

1 Answer

3

A fairly reliable approach, appropriate to iOS 3.2 and 4.0 and later, is as follows. You must be prepared to supply your scalable view (the chief subview of the scroll view) in any requested scale. Then in scrollViewDidEndZooming: you will remove the blurred scale-transformed version of this view and replace it with a new view drawn to the new scale.

You will need:

  • An ivar for maintaining the previous scale; let's call it oldScale

  • A way of identifying the scalable view, both for viewForZoomingInScrollView: and for scrollViewDidEndZooming:; here, I'm going to apply a tag (999)

  • A method that creates the scalable view, tags it, and inserts it into the scroll view (here's I'll call it addNewScalableViewAtScale:). Remember, it must do everything according to scale - it sizes the view and its subviews or drawing, and sizes the scroll view's contentSize to match.

  • Constants for the minimumZoomScale and maximumZoomScale. These are needed because when we replace the scaled view by the detailed larger version, the system is going to think that the current scale is 1, so we must fool it into allowing the user to scale the view back down.

Very well then. When you create the scroll view, you insert the scalable view at scale 1.0. This might be in your viewDidLoad, for example (sv is the scroll view):

[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;

Then in your scrollViewDidEndZooming: you do the same thing, but compensating for the change in scale:

UIView* v = [sv viewWithTag:999];
[v removeFromSuperview];
CGFloat newscale = scale * self->oldScale;
self->oldScale = newscale;
[self addNewScalableViewAtScale:newscale];
sv.minimumZoomScale = MIN / newscale;
answered 2010-11-12T05:33:14.393

Your Answer