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

Jacob Joz
  • 732
  • 1
  • 11
  • 29

3 Answers3

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;
}
liuyaodong
  • 2,547
  • 18
  • 31
  • Thanks, this answer is so good that I'd accept it twice if I could :) – Jacob Joz Aug 10 '13 at 11:44
  • 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? – Pedro Borges Sep 08 '14 at 05:56
  • @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. – liuyaodong Sep 09 '14 at 03:46
  • 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? – Pedro Borges Sep 09 '14 at 23:53
  • @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. – liuyaodong Sep 10 '14 at 05:36
  • 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? – Pedro Borges Sep 10 '14 at 16:23
  • @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. – liuyaodong Sep 10 '14 at 17:46
  • 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. – Pedro Borges Sep 10 '14 at 22:03
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;

    //  ...
}
bobnoble
  • 5,794
  • 3
  • 25
  • 32
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);
Rajesh Loganathan
  • 11,129
  • 4
  • 78
  • 90