[Swift5] IKImageViewによる画像のズームと表示位置のスクロール

2019年4月9日(更新: 2019年4月29日)

QuartzフレームワークIKImageView に表示した画像の一部を拡大表示し、その表示位置をスクロールできるようにする方法です。

IKImageViewについて

NSImageView に似ていますが、これと異なる点は、表示した画像のズーム(拡大縮小)や回転などを始めとした編集機能を持つことです。画像編集アプリを作成する際に便利な機能があらかじめ組み込まれた便利なクラスとなっています。

以下では、この IKImageView に NSScrollView を組み合わせて、Mac標準アプリの「プレビュー.app」に似た画像の拡大表示ができるサンプルを紹介します。

IKImageViewを利用した画像の拡大縮小表示

サンプルのソースコード

プロジェクトを新規作成して、ウィンドウの contentViewController となるクラスを作成します。ここでは MainViewController と名付けます。

MainViewController.swift

import Cocoa
import Quartz

extension NSSize {
    static func *(rect: NSSize, ratio: CGFloat) -> NSSize {
        return NSSize(width: rect.width * ratio, height: rect.height * ratio)
    }
}

class MainViewController : NSViewController {
    
    var prevMagnification: CGFloat = 1
    
    override func loadView() {
        
        // IKImageView の設定
        let imageView = IKImageView()
        
        // NSScrollView 内の領域
        let scrollContentView = NSClipView()
        scrollContentView.documentView = imageView
        
        // NSScrollView の本体
        let scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: 600, height: 400))
        scrollView.contentView = scrollContentView
        scrollView.hasVerticalScroller = true
        scrollView.hasHorizontalScroller = true
        scrollView.autohidesScrollers = false
        
        // サイズを調節
        imageView.frame = scrollView.frame
        // 初期の画像
        imageView.setImageWith(URL(fileURLWithPath: "読み込む画像のURL"))
        // ドラッグ&ドロップで画像を読み込む設定
        imageView.supportsDragAndDrop = true
        imageView.backgroundColor = NSColor.green
        
        // カスタムのズーム機能を設定
        imageView.addGestureRecognizer(NSMagnificationGestureRecognizer(target: self, action: #selector(self.magnificationGesture)))
        
        // NSScrollView をウィンドウの contentView とする
        self.view = scrollView
    }
    
    // ピンチジェスチャの設定
    @objc func magnificationGesture(_ gesture: NSMagnificationGestureRecognizer) {

        let imageView = gesture.view as! IKImageView
        
        // 前のズーム値を初期化
        if abs(gesture.magnification) < 0.01 {
            prevMagnification = 0
        }
        
        // ズームの変化量
        let delta: CGFloat = gesture.magnification - prevMagnification
        imageView.zoomFactor += delta
        
        // ズームした画像の大きさに合わせてIKImageViewのサイズを変化させる
        let currentImageSize = imageView.imageSize() * imageView.zoomFactor
        let currentRect = NSRect(origin: self.view.frame.origin, size: currentImageSize)
        if !self.view.frame.contains(currentRect) {
            // IKImageViewのサイズはウィンドウより小さくならないように
            imageView.frame.size = currentImageSize
        } else {
            imageView.frame = self.view.frame
        }
        
        prevMagnification = gesture.magnification
    }
}

このクラスを AppDelegate で contentViewController に指定します。

AppDelegate.swift

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {

    @IBOutlet weak var window: NSWindow!
    let mainViewController = MainViewController()

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        window.delegate = self
        window.contentViewController = mainViewController
    }

    ...

これで、冒頭のサンプルのように拡大縮小表示ができるアプリができます。IKImageView には画像を拡大した際にスクロールバーを表示して全体を見回せるようにする機能はないため NSScrollView を組み合わせてこれを実装しています。

フラグ supportsDragAndDrop を有効にすると、ドラッグ&ドロップで簡単に画像を読み込むことができます。

ドラッグ&ドロップでIKImageViewに画像を読み込む

以上、 IKImageViewによる画像のズームと表示位置のスクロールを行うサンプルの解説でした。

参考

IKImageView – Quartz | Apple Developer Documentation

コメントを残す

メールアドレスが公開されることはありません。