How do I reset after a UIScrollView zoom?

I can’t help you with the crashing, other than tell you to check and make sure you aren’t unintentionally autoreleasing a view or layer somewhere within your code. I’ve seen the simulator handle the timing of autoreleases differently than on the device (most often when threads are involved).

The view scaling is an issue with UIScrollView I’ve run into, though. During a pinch-zooming event, UIScrollView will take the view you specified in the viewForZoomingInScrollView: delegate method and apply a transform to it. This transform provides a smooth scaling of the view without having to redraw it each frame. At the end of the zoom operation, your delegate method scrollViewDidEndZooming:withView:atScale: will be called and give you a chance to do a more high-quality rendering of your view at the new scale factor. Generally, it’s suggested that you reset the transform on your view to be CGAffineTransformIdentity and then have your view manually redraw itself at the new size scale.

However, this causes a problem because UIScrollView doesn’t appear to monitor the content view transform, so on the next zoom operation it sets the transform of the content view to whatever the overall scale factor is. Since you’ve manually redrawn your view at the last scale factor, it compounds the scaling, which is what you’re seeing.

As a workaround, I use a UIView subclass for my content view with the following methods defined:

- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
    [super setTransform:newTransform];
}

- (void)setTransform:(CGAffineTransform)newValue;
{
    [super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}

where previousScale is a float instance variable of the view. I then implement the zooming delegate method as follows:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
    [contentView setTransformWithoutScaling:CGAffineTransformIdentity];
// Code to manually redraw view at new scale here
    contentView.previousScale = scale;
    scrollView.contentSize = contentView.frame.size;
}

By doing this, the transforms sent to the content view are adjusted based on the scale at which the view was last redrawn. When the pinch-zooming is done, the transform is reset to a scale of 1.0 by bypassing the adjustment in the normal setTransform: method. This seems to provide the correct scaling behavior while letting you draw a crisp view at the completion of a zoom.

UPDATE (7/23/2010): iPhone OS 3.2 and above have changed the behavior of scroll views in regards to zooming. Now, a UIScrollView will respect the identity transform you apply to a content view and only provide the relative scale factor in -scrollViewDidEndZooming:withView:atScale:. Therefore, the above code for a UIView subclass is only necessary for devices running iPhone OS versions older than 3.2.

Leave a Comment