Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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となるでしょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした