株式会社スタジオザンビ

スマホメニュー

ホームページ制作・ウェブアプリ制作 株式会社STUDIOザンビ 帯広/十勝/北海道

ホームページ作成・webシステム開発からデジタルコンテンツまで、付加価値の高いデザインやWEBシステムをご提供いたします。

BLOGブログ. 業務日誌

SwiftでピンチジェスチャーによるUIView拡大縮小

SwiftでピンチジェスチャーによるUIView拡大縮小

画像ビューワのようにUIScrollViewを使って、ピンチインでUIViewの拡大後に、ドラッグしてスクロールできるようにする。
すごく苦戦したので復習 …… 勉強、勉強の毎日である。

Xcode 6.3
Deployment Target 8.0

まず簡単にviewForZoomingInScrollView使ったパターン。
実際に作ったアプリは拡大時の処理が複雑だったので採用しなかった。

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {
    
    // view
    var baseScrollView:UIScrollView?=nil
    let baseScrollViewTag:Int=1
    var myView:UIView?=nil
    
    // viewのサイズ
    let viewWidth:CGFloat=300
    let viewHeight:CGFloat=300
    
    // 拡大率
    let zoomMin:CGFloat=1.0
    let zoomMax:CGFloat=5.0

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor=UIColor.whiteColor()
        
        // スクロールビュー
        baseScrollView=UIScrollView(frame: CGRectMake((self.view.frame.size.width-viewWidth)/2,(self.view.frame.size.height-viewHeight)/2,viewWidth,viewHeight))
        baseScrollView!.backgroundColor=UIColor.blackColor()
        baseScrollView!.decelerationRate=UIScrollViewDecelerationRateNormal
        baseScrollView!.minimumZoomScale=zoomMin
        baseScrollView!.maximumZoomScale=zoomMax
        baseScrollView!.tag=baseScrollViewTag
        baseScrollView!.delegate=self
        self.view.addSubview(baseScrollView!)
        
        // 適当にコンテンツビュー作る
        myView=UIView(frame: CGRectMake(0,0,viewWidth,viewHeight))
        baseScrollView!.addSubview(myView!)
        
        var imageview:UIImageView=UIImageView(frame: CGRectMake(0,0,viewWidth,viewHeight))
        imageview.image=UIImage(named: "test.png")
        myView!.addSubview(imageview)
        
        let subviewColor:Array<UIColor>=[UIColor.redColor(),UIColor.yellowColor(),UIColor.greenColor(),UIColor.blueColor()]
        let subviewMax:Int=subviewColor.count
        let subviewWidth:CGFloat=viewWidth/CGFloat(subviewMax)
        let subviewHeight:CGFloat=viewHeight/CGFloat(subviewMax)
        for var i=0 ; i < subviewMax ; i++ {
            var subview:UIView=UIView(frame: CGRectMake(subviewWidth*CGFloat(i),subviewHeight*CGFloat(i),subviewWidth,subviewHeight))
            subview.backgroundColor=subviewColor[i]
            myView!.addSubview(subview)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
        if scrollView.tag == baseScrollViewTag {
            return myView!
        }else{
            return nil
        }
    }
}

そして以下が実際に採用した、UIScrollViewDelegateを使わないtransformで拡大縮小するパターン。
UIPinchGestureRecognizerでピンチジェスチャーを登録している。
コードは長くなるがUIViewの拡大時に複雑な処理が必要なら向いてる気がする。

import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {
    
    // view
    var baseScrollView:UIScrollView?=nil
    var myView:UIView?=nil
    
    // viewのサイズ
    let viewWidth:CGFloat=300
    let viewHeight:CGFloat=300
    
    // 拡大率
    var zoomNow:CGFloat=1.0
    let zoomMin:CGFloat=1.0
    let zoomMax:CGFloat=5.0

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor=UIColor.whiteColor()
        
        // スクロールビュー
        baseScrollView=UIScrollView(frame: CGRectMake((self.view.frame.size.width-viewWidth)/2,(self.view.frame.size.height-viewHeight)/2,viewWidth,viewHeight))
        baseScrollView!.backgroundColor=UIColor.blackColor()
        baseScrollView!.decelerationRate=UIScrollViewDecelerationRateNormal
        self.view.addSubview(baseScrollView!)
        
        // 適当にコンテンツビュー作る
        myView=UIView(frame: CGRectMake(0,0,viewWidth,viewHeight))
        baseScrollView!.addSubview(myView!)
        
        var imageview:UIImageView=UIImageView(frame: CGRectMake(0,0,viewWidth,viewHeight))
        imageview.image=UIImage(named: "test.png")
        myView!.addSubview(imageview)
        
        let subviewColor:Array<UIColor>=[UIColor.redColor(),UIColor.yellowColor(),UIColor.greenColor(),UIColor.blueColor()]
        let subviewMax:Int=subviewColor.count
        let subviewWidth:CGFloat=viewWidth/CGFloat(subviewMax)
        let subviewHeight:CGFloat=viewHeight/CGFloat(subviewMax)
        for var i=0 ; i < subviewMax ; i++ {
            var subview:UIView=UIView(frame: CGRectMake(subviewWidth*CGFloat(i),subviewHeight*CGFloat(i),subviewWidth,subviewHeight))
            subview.backgroundColor=subviewColor[i]
            myView!.addSubview(subview)
        }
        
        // コンテンツビューにピンチジェスチャー登録
        let pinchGesture:UIPinchGestureRecognizer=UIPinchGestureRecognizer(target:self, action: "viewPinch:")
        pinchGesture.delegate=self
        myView!.addGestureRecognizer(pinchGesture)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    // ピンチジェスチャー
    func viewPinch(sender:UIPinchGestureRecognizer){
        // 拡大率を適当に決める
        zoomNow += (sender.scale-1)*0.5
        
        if zoomNow > zoomMax {
            zoomNow=zoomMax
        }else if zoomNow < zoomMin {
            zoomNow=zoomMin
        }
        
        // ピンチ中心座標
        var location:CGPoint=sender.locationInView(baseScrollView!)
        
        // 拡大処理
        self.viewZoom(zoomNow, ox:location.x, oy:location.y)
    }
    
    // 拡大処理
    func viewZoom(var z:CGFloat, ox:CGFloat, oy:CGFloat){
        if z <= 1 {
            baseScrollView!.contentSize=CGSizeMake(viewWidth,viewHeight)
            myView!.transform=CGAffineTransformIdentity
            myView!.frame.origin.x=0
            myView!.frame.origin.y=0
            baseScrollView!.contentOffset.x=0
            baseScrollView!.contentOffset.y=0
        }else{
            // 拡大前サイズ保存
            let prev_w:CGFloat=myView!.frame.size.width
            let prev_h:CGFloat=myView!.frame.size.height
            
            // 拡大(座標と一緒にやるのどっかで見た気がするのに忘れた)
            myView!.transform=CGAffineTransformMakeScale(CGFloat(z), CGFloat(z))
            myView!.frame.origin.x=0
            myView!.frame.origin.y=0
            
            // 拡大後のサイズ
            let zoom_w:CGFloat=myView!.frame.size.width
            let zoom_h:CGFloat=myView!.frame.size.height
            baseScrollView!.contentSize=CGSizeMake(zoom_w,zoom_h)
            
            // スクロールx座標計算
            let base_ox:CGFloat=ox-baseScrollView!.contentOffset.x
            var zoom_x:CGFloat=(ox*zoom_w/prev_w)-base_ox
            if zoom_x < 0 {
                zoom_x=0
            }else if (zoom_w-zoom_x) < viewWidth {
                zoom_x=zoom_w-viewWidth
            }
            
            // スクロールy座標計算
            let base_oy:CGFloat=oy-baseScrollView!.contentOffset.y
            var zoom_y:CGFloat=(oy*zoom_h/prev_h)-base_oy
            if zoom_y < 0 {
                zoom_y=0
            }else if (zoom_h-zoom_y) < viewHeight {
                zoom_y=zoom_h-viewHeight
            }
            
            //スクロール位置
            baseScrollView!.contentOffset.x=CGFloat(zoom_x)
            baseScrollView!.contentOffset.y=CGFloat(zoom_y)
        }
    }
    
}

どこかでtransformのナイスなexample読んだのに見つけられない…。

dispatch_async(dispatch_get_main_queue(),{ ... })とか絡んでくると取得できる座標とサイズが違うと思うので要注意。
↑のせいで実際に作ったアプリとは計算式が全然違って、復習にならない。

この感覚は、javascriptの「display:none」が原因のバグとすごく似ている。

  • ヒトコトどうぞ

    ※500文字以内

    ※スマートフォンなど画像認証がうまくいかない場合は、一度[変更]してからご入力ください。

    CAPTCHA Image

    英数字5桁(変更

    コメントされていません。