UIImageView inside UIScrollView Pans Too Far
Asked Answered
D

3

5

I have a problem where I have a UIImageView inside a UIScrollView. In Interface Builder, the UIScrollView takes up the entire screen, and the UIImageView takes up the entire UIScrollView. The problem is that when I have an image that is landscape oriented I have it set to aspect fit, so I have gray bars at the top and bottom (which is what I want). However when I zoom into the photo, once the photo zooms large enough to fit the screen vertical I want it to not pan into the gray zone that was above it. See the screen shots I have attached. I basically want it to work like the Photos app in that respect. Here is my code for setting up the UIScrollView and UIImageView:

- (void)viewDidLoad
{
   [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
   self.imageView.image = [UIImage imageNamed:@"IMG_0300.JPG"];
   self.imageView.contentMode = UIViewContentModeScaleAspectFit;
   self.scrollView.clipsToBounds = YES;
   self.scrollView.contentSize = self.imageView.bounds.size;
   self.scrollView.zoomScale = 1.0;
   self.scrollView.maximumZoomScale = 5.0;
   self.scrollView.minimumZoomScale = 1.0;

}

-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
   return self.imageView; 
}

See how it pans too far up

Thanks in advance.

Jacob

Dobb answered 9/8, 2013 at 12:38 Comment(0)
S
10

It is your whole UIImageView zoomed, not only its image, but also the gray bars. Your scrollView is just honestly reflecting this. The result you want probably can be done like this:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIImage *image = [UIImage imageNamed:@"IMG_0300.JPG"];
    CGFloat ratio = CGRectGetWidth(self.scrollView.bounds) / image.size.width;
    self.imageView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.scrollView.bounds), image.size.height * ratio);
    self.imageView.center = CGPointMake(CGRectGetMidX(self.scrollView.bounds), CGRectGetMidY(self.scrollView.bounds));
    self.imageView.image = image;
    self.scrollView.clipsToBounds = YES;
    self.scrollView.contentSize = self.imageView.bounds.size;
    self.scrollView.zoomScale = 1.0;
    self.scrollView.maximumZoomScale = 10.0;
    self.scrollView.minimumZoomScale = 1.0;

}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    UIView *subView = self.imageView;
    CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width)?
    (scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;

    CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height)?
    (scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;

    subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
                                 scrollView.contentSize.height * 0.5 + offsetY);
}

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}
Southeastward answered 9/8, 2013 at 13:21 Comment(8)
Thanks, this answer is so good that I'd accept it twice if I could :)Dobb
I implemented your code and in viewDidAppear:animated the UIImageView bounds are back to the size declared in the storyboard. Do you have a clue to what can be triggering this?Fruin
@PedroBorges If you are using auto-layout, this code snippet may not work and you should use NSLayoutConstraint to update the size of the views. Or you can disable auto-layout in the storyboard to see whether everything works as your expect -- this may help you locate the issue.Southeastward
Yes I understand that and yes I'm using auto layout but I have no constraints set for the UIImageView so why does it change size to match the containing UIScrollView?Fruin
@PedroBorges if you enabled auto-layout and haven't set constraints, they will be set automatically for you. Set frames or set constraints, they are exclusive.Southeastward
Oh I see, but i can't even set constraints for an uiimageview inside a uiscrollview since the uiscrollview's contents are not fixed. In a nutshell maybe if i programmatically remove the "automatic" constraints!? or can i just disable auto layout in the uiimageview and keep the constraints for the uiscrollview to super and top/bottom guides?Fruin
@PedroBorges i will suggest that you disable auto-layout of the view of the UIViewController first to make sure it is the auto-layout that caused the issue.Southeastward
That'll mess up the whole storyboard since I am using auto layout on each and every other view, gonna try through the constraints, thanks.Fruin
R
0

Rather than "have gray bars at the top and bottom", set the background color of the UIScrollView to gray.

Also, don't use view dimensions in viewDidLoad as the subviews are not yet laid out. Move it to didLayoutSubviews or viewWillAppear. Also, set the UIScrollView contentSize based on your maximum zoom. For example:

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    self.scrollView.backgroundColor = [UIColor lightGrayColor];  // or other color
    CGFloat maxScale = 5.0;
    self.scrollView.zoomScale = 1.0;
    self.scrollView.maximumZoomScale = maxScale;
    self.scrollView.minimumZoomScale = 1.0;
    CGSize contentSize = CGSizeMake(self.scrollView.bounds.size.width * maxScale, self.scrollView.bounds.size.height * maxScale); 
    self.scrollView.contentSize = contentSize;

    //  ...
}
Reposition answered 9/8, 2013 at 13:22 Comment(0)
C
0

Try this:

UIView *subView = self.imageView;
    CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width)?
    (scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;

    CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height)?
    (scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;

    subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX,
                                 scrollView.contentSize.height * 0.5 + offsetY);
Charlatanism answered 26/11, 2013 at 9:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.