Swift3で画像を拡大縮小、スクロールさせる #Swift3 #iOS

技術話

Swift3で、ピンチイン、アウトで画像の拡大縮小、スクロールをさせる必要があり、いくつか試行錯誤したのでその覚書です。

ソースコード

サンプルの画像を一枚表示して、拡大、縮小、スクロールするだけのデモアプリです。ソースコードは以下の通りです。

//
//  ViewController.swift
//  zoom-scroll-demo-app
//
//  Created by Tetsuo Yutani on 2018/01/04.
//  Copyright © 2018 Tetsuo Yutani. All rights reserved.
//

import UIKit
import os.log

class ViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    
    var imageView: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        scrollView.delegate = self

        // Scale factor can be changed from 0.0 to 2.0
        scrollView.minimumZoomScale = 0.0
        scrollView.maximumZoomScale = 2.0

        imageView = UIImageView(image: UIImage(named: "sample.jpg"))
        imageView.contentMode = .scaleAspectFit
        scrollView.addSubview(imageView)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        if let image = imageView.image {
            let w_scale = scrollView.frame.width / image.size.width
            let h_scale = scrollView.frame.height / image.size.height

            // Fit longer edge to screen
            // let scale = min(w_scale, h_scale)

            // Fit shorter edge to screen
            let scale = max(w_scale, h_scale)
           
            // Not zoom, only scroll
            // scrollView.minimumZoomScale = scale
            // scrollView.maximumZoomScale = scale
            
            scrollView.zoomScale = scale
            scrollView.contentSize = imageView.frame.size

            // In case that the image is larger than screen, calculate offset to show the center of image at initial launch
            let offset = CGPoint(x: (imageView.frame.width - scrollView.frame.width) / 2.0, 
                                 y: (imageView.frame.height - scrollView.frame.height) / 2.0)
            scrollView.setContentOffset(offset, animated: false)
        }
    }
    
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
    
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        // Keep the image at center of the screen in case that the image is smaller than the screen
        scrollView.contentInset = UIEdgeInsetsMake(
            max((scrollView.frame.height - imageView.frame.height) / 2.0, 0.0),
            max((scrollView.frame.width - imageView.frame.width) / 2.0, 0.0),
            0,
            0
        );
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

デモアプリの全コードはgithubに置いています。

Storyboard

このアプリはUIScrollViewの上にUIImageViewを置いて、そこに画像を表示させています。ですので、storyboardでViewControllerの前面にUIScrollViewを置きます。
次に、UIScrollViewを選択してAttributes inspectorを表示させます。アトリビュートの中の”Zoom”のMinとMaxの値を変更します。デフォルトでは両方とも1ですので、そのままでは拡大も縮小もできません。デモアプリではコード内で以下のように設定していますが、そうしない場合はこのアトリビュートを適切に設定しなければなりません。

// Scale factor can be changed from 0.0 to 2.0
scrollView.minimumZoomScale = 0.0
scrollView.maximumZoomScale = 2.0

さらにその上にUIImageViewを配置しても良いですが、デモアプリでは次のようにコード内でUIImageViewを配置しています。

imageView = UIImageView(image: UIImage(named: "sample.jpg"))
imageView.contentMode = .scaleAspectFit
scrollView.addSubview(imageView)

拡大縮小

addSubView() を使ったので、viewDidLayoutSubview の中で拡大縮小の設定等をしています。デフォルトのscaleは1なので、そのままでは画像がdot by dotで表示されてしまいます。画像の大きさとScrollViewの大きさから縮小率を計算して、scrollView.zoomScale に設定します。

let w_scale = scrollView.frame.width / image.size.width
let h_scale = scrollView.frame.height / image.size.height

// Fit longer edge to screen
// let scale = min(w_scale, h_scale)

// Fit shorter edge to screen
let scale = max(w_scale, h_scale)

w_scaleとh_scaleの小さい方を使えば画像全体が表示されます。逆に、大きい方を使えば画像が画面いっぱいに表示されます。

なお、拡大縮小したくないときは、ズームの上限と下限を同じに設定します。

// Not zoom, only scroll
scrollView.minimumZoomScale = scale
scrollView.maximumZoomScale = scale

最後に、拡大縮小した画像を表示するためには次のコードが必要です。

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imageView
}

画像を画面の中央に表示する

画像を縮小して画面より小さくすると、画像は画面の左上に表示されてしまいます。常に画像を画面の中央に表示するためには次のようにします。

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    // Keep the image at center of the screen in case that the image is smaller than the screen
    scrollView.contentInset = UIEdgeInsetsMake(
        max((scrollView.frame.height - imageView.frame.height) / 2.0, 0.0),
        max((scrollView.frame.width - imageView.frame.width) / 2.0, 0.0),
        0,
        0
    );
}

画面より大きな画像の中央を表示する

画面より大きな画像の場合、最初に表示された時、画像の左側が表示されてしまいます。画像の中央を表示したい場合は、viewDidLayoutSubviewで以下のようにします。

// In case that the image is larger than screen, calculate offset to show the center of image at initial launch
let offset = CGPoint(x: (imageView.frame.width - scrollView.frame.width) / 2.0, 
                     y: (imageView.frame.height - scrollView.frame.height) / 2.0)
scrollView.setContentOffset(offset, animated: false)

最後に

UIScrollViewを使うだけで簡単に拡大縮小とスクロールを実現できました。もっと細かい制御をしたい場合はジェスチャーを使うのが良さそうですが、通常の用途はUIScrollViewだけで十分でしょう。

 

 

 

 

 

コメント

  1. […] Scroll View Programming Guide for iOS UIScrollView And Autolayout Swift3で画像を拡大縮小、スクロールさせる […]

タイトルとURLをコピーしました