barley barley - 18 days ago 9
iOS Question

content jumps on zooming out with UIScrollView

I want help with my UIScrollView sample.

I created a simple program that scrolls and zooms the content (

UIImageView
). It works fine, except that the content frequently disappears to the right-bottom when I try zooming out. But since I set
minimumZoomScale
to 1.0f, it is actually not zooming out, only the content is jumping out of the view. And what is even more weird is that I cannot scroll up after this. Apparently content size is messed up as well.

enter image description here

The setup I have in my sample code is as in the figure below.

enter image description here

When I checked the status after (trying) zooming out, I found two wrong things.


  • _scrollView.contentSize
    is 480x360, which should not be smaller than 1000x1000

  • _scrollView.bounds
    jumped to the top somehow (i.e.,
    _scrollView.bounds.origin.y
    is always 0)



To cope with the two items above, I added following code in my UIScrollViewDelegate and now it works fine.

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
if(scrollView == _scrollView && view == _contentView)
{
// Setting ivars for scrollViewDidZoom
_contentOffsetBeforeZoom = _scrollView.contentOffset;
_scrollViewBoundsBeforeZoom = _scrollView.bounds;
}
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if(scrollView == _scrollView)
{
// If you zoom out, there are cases where ScrollView content size becomes smaller than original,
// even though minimum zoom scale = 1. In that case, it will mess with the contentOffset as well.
if(_scrollView.contentSize.width < CONTENT_WIDTH || _scrollView.contentSize.height < CONTENT_HEIGHT)
{
_scrollView.contentSize = CGSizeMake(CONTENT_WIDTH, CONTENT_HEIGHT);
_scrollView.contentOffset = _contentOffsetBeforeZoom;
}

// If you zoom out, there are cases where ScrollView bounds goes outsize of contentSize rectangle.
if(_scrollView.bounds.origin.x + _scrollView.bounds.size.width > _scrollView.contentSize.width ||
_scrollView.bounds.origin.y + _scrollView.bounds.size.height > _scrollView.contentSize.height)
{
_scrollView.bounds = _scrollViewBoundsBeforeZoom;
}
}
}


However, does it need to come down to this? This is a very simple sequence, and it is hard to believe that Apple requires us to put this kind of effort. So, my bet is I am missing something here...

Following is my original code. Please help me find what I am doing wrong (or missing something)!

#define CONTENT_WIDTH 1000
#define CONTENT_HEIGHT 1000

>>>> Snip >>>>

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
_scrollView.contentSize = CGSizeMake(CONTENT_WIDTH, CONTENT_HEIGHT);
_scrollView.backgroundColor = [UIColor blueColor];
_scrollView.maximumZoomScale = 8.0f;
_scrollView.minimumZoomScale = 1.0f;
_scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
_scrollView.scrollEnabled = YES;
_scrollView.delegate = self;
[self.view addSubview:_scrollView];

_contentView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"sample.jpg"]]; // sample.jpg is 480x360
CGPoint center = (CGPoint){_scrollView.contentSize.width / 2, _scrollView.contentSize.height / 2};
_contentView.center = center;
[_scrollView addSubview:_contentView];

_scrollView.contentOffset = (CGPoint) {center.x - _scrollView.bounds.size.width / 2, center.y - _scrollView.bounds.size.height / 2};
}

- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
if(scrollView == _scrollView)
{
return _contentView;
}
return nil;
}

Answer

I created a quick sample project and had the same issue you described using the code you pasted. I don't exactly know what the "proper" way to zoom is in iOS but I found this tutorial which says that you need to recenter your contentView after the scrollView has been zoomed. I would personally expect it to be automatically re-centered given that it is the view you're returning in the viewForZoomingInScrollView delegate method but apparently not.

- (void)centerScrollViewContents {
    CGSize boundsSize = _scrollView.bounds.size;
    CGRect contentsFrame = _contentView.frame;

    if (contentsFrame.size.width < boundsSize.width) {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
    } else {
        contentsFrame.origin.x = 0.0f;
    }

    if (contentsFrame.size.height < boundsSize.height) {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
    } else {
        contentsFrame.origin.y = 0.0f;
    }

    _contentView.frame = contentsFrame;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    // The scroll view has zoomed, so we need to re-center the contents
    [self centerScrollViewContents];
}

The code above is not written by me but is simply copied from the tutorial. I think its pretty straightforward. Also, centring the contentView seems to be a lot more elegant then constantly changing the bounds and content size of the scrollview so give it a try.

Comments