74
65

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UIScrollViewの中央にUIImageViewを配置しつつズーム可能にする

Last updated at Posted at 2016-02-08

はじめに

iOSアプリで画像Viewerを作るとき、UIScrollView上にUIImageViewを配置するのが一般的かと思います。
しかし単純に乗せただけでは、画像の起点が左上になったり、ズームすると画像外の場所までスクロールしてしまって、しっくりこない場合があります。

そこで、画像をUIScrollViewの中央に表示しつつ、正しくズーム/スクロールできる方法を考えました。

fox.gif

方針

簡単に方針としては

  • UIImageViewの位置は(0,0)
  • UIImageViewのサイズは、UIImageのサイズに合わせる(要調整)
  • UIScrollView.contentSizeは、UIImageViewのサイズに合わせる
  • UIScrollView.contentInsetを操作して、画像を常に中央に配置

要するにスクロールビューの余白を調整して常に中央に表示する感じです。

コード

ViewController.swift
class ViewController: UIViewController, UIScrollViewDelegate {
    @IBOutlet var scrollView: UIScrollView!
    var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
        
        imageView = UIImageView(image: UIImage(named: "test.png"))
        scrollView.addSubview(imageView)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if let size = imageView.image?.size {
            // imageViewのサイズがscrollView内に収まるように調整
            let wrate = scrollView.frame.width / size.width
            let hrate = scrollView.frame.height / size.height
            let rate = min(wrate, hrate, 1)
            imageView.frame.size = CGSizeMake(size.width * rate, size.height * rate)

            // contentSizeを画像サイズに設定
            scrollView.contentSize = imageView.frame.size
            // 初期表示のためcontentInsetを更新
            updateScrollInset()
        }
    }
    
    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
        // ズームのために要指定
        return imageView
    }
    
    func scrollViewDidZoom(scrollView: UIScrollView) {
        // ズームのタイミングでcontentInsetを更新
        updateScrollInset()
    }
    
    private func updateScrollInset() {
        // imageViewの大きさからcontentInsetを再計算
        // なお、0を下回らないようにする
        scrollView.contentInset = UIEdgeInsetsMake(
            max((scrollView.frame.height - imageView.frame.height)/2, 0),
            max((scrollView.frame.width - imageView.frame.width)/2, 0),
            0,
            0
        );
    }
}

なお、UIScrollViewはStoryBoardで設置、UIImageViewはコード内でaddSubview()しています。
これは、UIImageViewの設置をStoryBoard側で行うと、画像のframeとcontentSizeの設定が入り混じり複雑になってしまうことを避けるためです。

StoryBoard側で、UIScrollViewのzoom max/min値を指定することを忘れずに。
これを忘れるとどうあがいてもズームしません。

補足

ナビゲーションバーなどを考慮する場合は、contentInsetの再計算に別途調整が必要です。
例えばステータスバー+ナビゲーションバーであればcontentInset.topの最低値は0→64となるでしょう。

74
65
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
74
65

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?