2
votes

I want to achieve a UIScrollview, that contains an UIImageView that is centered and zooms without flickering, with only using Layout-Constraints.

I've created a UIScrollView and the UIImageView add this as subviews to a normal UIView.

self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self.scrollView.maximumZoomScale = 10.0f;
self.scrollView.delegate = self;

self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;

[self.scrollView addSubview:self.imageView];
[self.view addSubview:self.scrollView];

Than I add the Constraints for these Views:

    NSDictionary *viewsDictionary = @{@"scrollView" : self.scrollView,
                                  @"imageView" : self.imageView};

CGRect imageFrame = CGRectMake(0, 0, 184, 111);
NSDictionary *metrics = @{@"width" : @(self.view.frame.size.width),
                          @"height" : @(self.view.frame.size.height),
                          @"imageWidth" : @(widgetFrame.size.width),
                          @"imageHeight" : @(widgetFrame.size.height)};

NSArray *scrollViewVerticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView(height)]" options:0 metrics:metrics views:viewsDictionary];
NSArray *scrollViewHorizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView(width)]" options:0 metrics:metrics views:viewsDictionary];

NSArray *imageViewVerticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[imageView(imageHeight)]" options:0 metrics:metrics views:viewsDictionary];
NSArray *imageViewHorizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[imageView(imageWidth)]" options:0 metrics:metrics views:viewsDictionary];

NSLayoutConstraint *imageCenterXConstraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
NSLayoutConstraint *imageCenterYConstraint = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];

[self.view addConstraints:scrollViewVerticalConstraints];
[self.view addConstraints:scrollViewHorizontalConstraints];

[self.view addConstraints:imageViewVerticalConstraints];
[self.view addConstraints:imageViewHorizontalConstraints];

[self.view addConstraints:@[imageCenterXConstraint, imageCenterYConstraint]];

Now it looks like it should... a centered UIImageView within a UIScrollView that can be zoomed (through the UIScrollViewDelegate method).

But I have a problem when I finished the Zoom-Gesture the UIImageView shortly move to a other position and returns immediately to the center. By zooming in it moves in the direction to the top left corner, by zooming out in the direction of the bottom right corner.

Why the hell it do that?

Can somebody give me a hint? I looked through SO but found no answer in relation to a centered UIImageView.

1
There's a pretty easy to follow Ray Wenderlich tutorial for doing exactly this. I'll find the link. EDIT: raywenderlich.com/10518/…nhgrif
Take a look at MWPhotoBrowser, it's MWZoomingScrollView file. It does exactly that. You can easily make out how to do it, just by studying the code. Hint : It depends on layoutSubviews.n00bProgrammer
I looked at your resources but it doesn't help me... Thank you anywaymatzino

1 Answers

0
votes

I worked on a similar kind of task. The best way that I can suggest is using a subclass of scroll view. You can define a property imageView in this class. I did the same and used the following code snippet inside the class PhotoScrollView which extends UIScrollView:

//In viewDidLoad

    self.minimumZoomScale = 1.0f;
    self.maximumZoomScale = 15.0f;
    self.zoomScale = 1.0f;

    self.delegate   =   self;

//In your doubleTapped method
 //for zooming after double tapping
 - (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer
  {
   // get the point of image which is double tapped

   CGPoint pointInView = [recognizer locationInView:self.imageView];

    // Get a zoom scale that's zoomed in slightly, capped at the maximum zoom scale specified by the scroll view
    CGFloat newZoomScale = self.zoomScale * 4.0f;
    newZoomScale = MIN(newZoomScale, self.maximumZoomScale);

    // Figure out the rect we want to zoom to, then zoom to it

    CGSize scrollViewSize = self.imageView.frame.size;


    CGFloat w = scrollViewSize.width / newZoomScale;
    CGFloat h = scrollViewSize.height / newZoomScale;
    CGFloat x = pointInView.x - (w / 2.0f);
    CGFloat y = pointInView.y - (h / 2.0f);

    CGRect rectToZoomTo = CGRectMake(x, y, w, h);

    [self zoomToRect:rectToZoomTo animated:YES];
  }
// To re-center the image after zooming in and zooming out
- (void)centerScrollViewContents
{
    CGSize boundsSize = self.bounds.size;
    CGRect contentsFrame = self.imageView.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;
    }

  self.imageView.frame = contentsFrame;
}

Also you have to return your imageView in the following method:

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

But if you don't want to use Custom ScollView as I suggested, you can use your property scrollView instead of self everywhere inside this code snippet.

Please let me know if it works. Thanks :)