16857
UIScrollViewの上にUIImageViewを乗せてピンチで縮小/拡大とダブルタップで最大倍率/最小倍率  
というズームをしているのですが、画像の高さが小さい画像でズームすると  
下の写真のように、何もない領域を表示できてしまいます。  
試行錯誤してみましたが解決策がわからず質問させて頂きました。

解決策のご教授お願い致します。

* * *

## 画像

[自アプリズーム前]......(問題発生)[自アプリズーム後]......(目標)[デフォルト写真アプリズーム後]

[![画像の説明をここに入力](https://i.stack.imgur.com/jx7Ah.png)](https://i.stack.imgur.com/jx7Ah.png)
[![画像の説明をここに入力](https://i.stack.imgur.com/Q7jZW.png)](https://i.stack.imgur.com/Q7jZW.png)
[![画像の説明をここに入力](https://i.stack.imgur.com/V9H2k.png)](https://i.stack.imgur.com/V9H2k.png)

* * *

## 現状

[![レイヤー](https://i.stack.imgur.com/zHKvG.png)](https://i.stack.imgur.com/zHKvG.png)

  1. AutoLayout使用なし
  2. view contentMode →scaleToFill  
scrollView contentMode→scaleToFill  
ImageView→AspectFit

  3. self.view.frame == self.scrollview.frame == self.mainImageView.frame;  
三つとも同じフレームで被せています。

※RMP~はナビゲーションバーを使ったカスタム画面移動のOSSです。  
[RMPZoomTransitionAnimator/GitHub](https://github.com/recruit-
mp/RMPZoomTransitionAnimator)

* * *

## 当該コード

< DetailViewController.h>

```

    #import <UIKit/UIKit.h>
    #import "RMPZoomTransitionAnimator.h"
    @interface DetailViewController : UIViewController<RMPZoomTransitionAnimating, RMPZoomTransitionDelegate>
    
    @property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
    @property (nonatomic, weak) IBOutlet UIImageView *mainImageView;
    
    @end
    
```

< DetailViewController.m>

```

    #import "DetailViewController.h"
    #define DEFAULT_ZOOM_SCALE 1.0
    #define SCALE_RATE 3.0
    
    @interface DetailViewController ()<UIScrollViewDelegate>
    //zoom when doubleTap.
    - (void)onDoubleTapOnPhoto:(id)sender;
    @end
    
    @implementation DetailViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.scrollView.minimumZoomScale = DEFAULT_ZOOM_SCALE;
        self.scrollView.maximumZoomScale = SCALE_RATE;
        self.scrollView.delegate = self;
    
        UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDoubleTapOnPhoto:)];
        doubleTap.numberOfTapsRequired = 2;
        [self.view addGestureRecognizer:doubleTap];
    
    
        // Do any additional setup after loading the view.
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    - (void)onDoubleTapOnPhoto:(id)sender
    {
    
        float zoom = self.scrollView.zoomScale;
    
        CGPoint tapPoint = [sender locationInView:self.scrollView];
    
        // Zoom=self.scrollView.zoom → Zoom=1.0
        tapPoint.x /= zoom;
        tapPoint.y /= zoom;
    
        if (zoom >self.scrollView.minimumZoomScale)
        {
            // already max zoom : set zoom to default
            [self.scrollView setZoomScale:DEFAULT_ZOOM_SCALE animated:YES];
        } else
        {
            // zoom up
    
            float newZoom = zoom * SCALE_RATE;
            if (newZoom >= self.scrollView.maximumZoomScale)
            {
                newZoom = self.scrollView.maximumZoomScale;
            }
    
            CGRect newRect = [self zoomRectForScrollView:self.scrollView
                                               withScale:newZoom
                                              withCenter:tapPoint];
            [self.scrollView zoomToRect:newRect animated:YES];
        }
    }
    
    #pragma - scrollViewZooming
    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
    {
        return self.mainImageView;
    }
    
    - (CGRect)zoomRectForScrollView:(UIView *)scrollView withScale:(float)scale withCenter:(CGPoint)center {
    
        CGRect zoomRect;
    
        // The zoom rect is in the content view's coordinates.
        // At a zoom scale of 1.0, it would be the size of the
        // imageScrollView's bounds.
        // As the zoom scale decreases, so more content is visible,
        // the size of the rect grows.
        zoomRect.size.height = scrollView.frame.size.height / scale;
        zoomRect.size.width  = scrollView.frame.size.width  / scale;
    
        // choose an origin so as to get the right center.
        zoomRect.origin.x = center.x - (zoomRect.size.width  / 2.0);
        zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
    
        return zoomRect;
    }
    
    #pragma mark - <RMPZoomTransitionAnimating>
    
    - (UIImageView *)transitionSourceImageView
    {
        UIImageView *imageView = [[UIImageView alloc] initWithImage:self.mainImageView.image];
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        imageView.clipsToBounds = YES;
        imageView.userInteractionEnabled = NO;
        imageView.frame = self.mainImageView.frame;
    
        return imageView;
    }
    
    - (UIColor *)transitionSourceBackgroundColor
    {
        return self.view.backgroundColor;
    }
    
    - (CGRect)transitionDestinationImageViewFrame
    {
        return self.mainImageView.frame;
    }
    
    #pragma mark - <RMPZoomTransitionDelegate>
    
    - (void)zoomTransitionAnimator:(RMPZoomTransitionAnimator *)animator
             didCompleteTransition:(BOOL)didComplete
          animatingSourceImageView:(UIImageView *)imageView
    {
        self.mainImageView.image = imageView.image;
    
    }
    @end
    
```
CC BY-SA 3.0
2015-09-20T04:17:35.263
16787
2015-09-22T21:12:13.727
2015-09-21T09:09:02.703
7362
8320
question
ios objective-c uiscrollview uiimageview
UIScrollView上でズームした時のUIImageViewをセンタリングorリサイズしたい
```

    // - (void)onDoubleTapOnPhoto:(id)sender
    - (void)onDoubleTapOnPhoto:(UITapGestureRecognizer *)sender
    {
        float zoom = self.scrollView.zoomScale;
    
        CGPoint tapPoint = [sender locationInView:self.scrollView];
    
```

このように、メソッドの引数を変更してみてください。  
メソッド`locationInView:`は、`UIGestureRecognizer`のメソッドなので、`sender`の型は`UIGestureRecognizer`かそのサブクラスでなければ、機能しません。
CC BY-SA 3.0
2015-09-21T09:14:53.213
16814
2015-09-21T09:14:53.213
7362
16787
answer
自己解決する事が出来ました。  
この解決法のポイントは以下

> **1\. UIImageViewはIBから生成しない(プログラムから生成する)**  
>  2\. リサイズする  
>  3\. 初期化後とズーム後にセンタリングする

* * *

[detailView.h]

```

    #import <UIKit/UIKit.h>
    #import "RMPZoomTransitionAnimator.h"
    @interface DetailViewController : UIViewController<RMPZoomTransitionAnimating, RMPZoomTransitionDelegate>
    
    @property (weak, nonatomic) IBOutlet UIScrollView *mainScrollView;
    //IBOutlet辞めました
    @property (nonatomic, strong) UIImageView *mainImageView;
    
    @end
    
```

[DetailView.m]書き換え分のみ

```

    //僕の場合、カスタム画面移動の具合でこのメソッドでimageが受け渡されるのでここでUIImageViewを生成しています
    //普通はviewDidLoadでUIImageを作って入れても問題ないと思います。
    - (void)zoomTransitionAnimator:(RMPZoomTransitionAnimator *)animator
             didCompleteTransition:(BOOL)didComplete
          animatingSourceImageView:(UIImageView *)imageView
    {
        UIImage *resizedImage = [self.class resizeAspectFitWithSize:imageView.image size:CGSizeMake(self.mainScrollView.frame.size.width, self.mainScrollView.frame.size.height)];
        self.mainImageView = [[UIImageView alloc]initWithImage:resizedImage];
        [self.mainScrollView addSubview:self.mainImageView];
        [self centeringContents];
        NSLog(@"imagewidth:%f imageheight:%f",_mainImageView.image.size.width,_mainImageView.image.size.height);
        NSLog(@"viewwidth:%f viewheight:%f",_mainImageView.frame.size.width,_mainImageView.frame.size.height);
    
    
    }
    
    + (UIImage*)resizeAspectFitWithSize:(UIImage *)srcImg size:(CGSize)size {
    
        CGFloat widthRatio  = size.width  / srcImg.size.width;
        CGFloat heightRatio = size.height / srcImg.size.height;
        CGFloat ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio;
    
        CGSize resizedSize = CGSizeMake(srcImg.size.width*ratio, srcImg.size.height*ratio);
    
        UIGraphicsBeginImageContextWithOptions(resizedSize, NO, 0);
        [srcImg drawInRect:CGRectMake(0, 0, resizedSize.width, resizedSize.height)];
        UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    
        UIGraphicsEndImageContext();
    
        return resizedImage;
    }
    
    - (void)scrollViewDidZoom:(UIScrollView *)scrollView
    {
        [self centeringContents];
    }
    - (void)centeringContents
    {
        CGSize boundsSize = self.mainScrollView.bounds.size;
        CGRect frameToCenter = self.mainImageView.frame;
    
        // center horizontally
        if (frameToCenter.size.width < boundsSize.width)
        {
            frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
        } else {
            frameToCenter.origin.x = 0;
        }
    
        // center vertically
        if (frameToCenter.size.height < boundsSize.height)
        {
            frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
        } else {
            frameToCenter.origin.y = 0;
        }
        NSLog(@"x:%f y:%f width:%f height:%f",frameToCenter.origin.x,frameToCenter.origin.y,frameToCenter.size.width,frameToCenter.size.height);
        self.mainImageView.frame = frameToCenter;
    
    }
    
```

* * *

やけくそになっていじくってたら出来たので成功理由は定かでないです。  
ただ、IB利用時にはセンタリングのメソッドを使ってもうまくいかなかった事を考えると

  * contentMode(ImageViewの)
  * ImageViewのframe
  * UIImageViewのinitWithImageの自動リサイズがうまく作用した

のどれかかな。と考えています。
CC BY-SA 3.0
2015-09-22T20:49:14.217
16857
2015-09-22T21:12:13.727
2015-09-22T21:12:13.727
8320
8320
16787
answer