views:

750

answers:

3

Hi,

I have a UIScrollView with the requirement that, when zooming, the contentSize.height should remain the same. Zooming in from 200x100 should result in a new contentSize of 400x100 instead of 400x200, for instance. I'd like to do my own drawing while the user is zooming.

I don't think I can use the normal zooming behaviour of UIScrollView to achieve this, so I'm trying to roll my own. (I could just let it do its thing and then redraw my contents when -scrollViewDidEndZooming:withView:atScale: gets called, but that wouldn't be very pretty).

Currently I am subclassing UIScrollView and trying to do my own zooming when two fingers are on the screen:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([touches count] != 2) {
        [super touchesMoved:touches withEvent:event];
    } else {
     // do my own stuff
    }
}

I thought that by overriding touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent: and touchesCancelled:withEvent: in this way should work, but it doesn't.

An earlier failed attempt was to place a transparent view on top of the scrollview and send touches that I'm not interested in to the scrollview :

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([touches count] != 2) {
        [self.theScrollView touchesMoved:touches withEvent:event];
    } else {
     // do my own stuff
    }
}

Any help would be appreciated.

Thanks, Thomas

A: 

I'm not sure what you mean by this:

I thought that by overriding touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent: and touchesCancelled:withEvent: in this way should work, but it doesn't.

Do you not get the events? You should receive the events, but I think there is a logic error in your if statement that may have been preventing this from working.

if ([touches count] != 2)

This is a problem, because the likelihood of the two touches happening precisely the same time is low. You'll want to be able to handle when touches happen independently, as well as when a user holds a finger stationary, and moves the other one. In these scenarios (which are common) you may only get one touch in that NSSet, even though the other is still valid.

The solution to handling touches properly is to remember some things about which touches came in and which touches left. Remember, the address of the UITouch does not change for the life of the touch, so you can safely compare addresses to ensure you are still dealing with the same touch as before, and track it's lifecycle.

If you are not getting the touch events, then that is a different problem altogether, you may need to turn set multiTouchEnabled:YES

slf
I was basing this on a chapter in Beginning iPhone 3 Programming where the touches NSSet was being used to access the touches. I wasn't aware that this NSSet might not contain all of the touches each time touchesMoved:withEvent: is called.
Thomas Müller
I had multiTouchEnabled on, but it seems to me that UIScrollView does some fancy stuff there behind the scenes. Could it be possible that it adds a new view on top of itself to handle all the touch events once the user starts scrolling or zooming? That was my idea, but I haven't really spent much time looking further into this theory.
Thomas Müller
A: 

You probably won't be able to maintain decent performance during zooming if you attempt to redraw your content on every frame of a pinch-zooming event. I'd recommend taking the approach of letting the UIScrollView zoom a scaled version of your drawing in or out, then redraw the content to be sharp at the end of the zoom in the -scrollViewDidEndZooming:withView:atScale: delegate method. This is what I do in my application, and it ends up working very well.

There are some tricks to resizing your content view properly at the end of a zoom, which I describe in this answer. Basically, you need to intercept the setting of a transform to your content view so that you can set it to a scale factor of 1 when you redraw your content. You'll also need to keep track of that scale factor, because the UIScrollView doesn't, and use that scale factor to adjust the transform that UIScrollView tries to apply to your content view with subsequent zoom operations.

You could use a modification of this technique to force a redraw of your content during the pinch-zooming, but in my tests this ended up being far too jerky to provide a good user experience.

Brad Larson
OK, thanks, I'll try intercepting setTransform: and redraw the view at the end of the pinch-zooming. I might try redrawing my view completely and see what the performance looks like. Won't be able to do this today, but I'll let you know how I go.
Thomas Müller
I did a quick test, intercepted setTransform:, and tried redrawing my content views each time. You're right, that's not an option on the device for performance reasons. I'll go with your recommended approach of letting UIScrollView zoom, and then redraw in -scrollViewDidEndZooming:withView:atScale:. Thanks.
Thomas Müller
A: 

I'm trying to do the same thing as this and I really want to be able to redraw while it's zooming. Fixing it up at the end in scrollViewDidEndZooming:withView:atScale is not really good enough.

The way I do it is pass a dummy view in viewForZoomingInScrollView: and read the height of this dummy view and set the frame on the actual content view to whatever I want. Because the frame changes, it means that drawRect gets called everytime. It seems fine on the simulator, I'm just drawing a few lines. But I don't actually own a device though, so I can't test it properly.

Also in the code you've got, you have touchesBegan:withEvent: but then you are forwarding to super touchesMoved:withEvent: instead of touchesBegan:withEvent:

moshy
I tried redrawing my content view each time, and while it works fine in the simulator, it's not even remotely acceptable on my iPhone 3G. With the data I tested with I was drawing just 9 CGPaths. My UIScrollView is currently transparent, though, which probably has quite an impact on performance. Anyway, you should try to test your code on an actual device - I wouldn't be surprised if redrawing during zooming will be too slow in your case as well.
Thomas Müller