前回UIScrollViewを使った画像拡大の実装方法を書きましたが、今回は別の方法で実装してみます

【Swift】ピンチイン・アウト・ダブルタップで画像を拡大する1(UIScrollView使用バージョン)
ピンチイン・アウトとダブルタップで画像(UIImageView)を拡大する方法です
UIScrollViewにUIImageViewを設置する形で実装します
ズーム後の位置の補正はこちらを参考にさせていただきました
【iOS開発】...
今回の実装では、前回の不満点だったダブルタップでのズーム位置のズレが解消されています
前回の不満点
今回の実装
仕様はこんな感じです
- ドラッグで移動する
- ピンチインで拡大
- ピンチアウトで縮小
- ダブルタップでスケールをその時点の2倍にする
- スケールが5倍以上になった場合は等倍に戻す
- スケールが等倍以下の場合は等倍に戻す
ジェスチャーを登録
画像を配置して、パン、ピンチイン・アウト、ダブルタップのジェスチャーを登録します
func myInit() { // 画像を読み込んで配置する let image = UIImage(named: "photo") imageView = UIImageView(image: image!) imageView.userInteractionEnabled = true imageView.contentMode = UIViewContentMode.ScaleAspectFit self.addSubview(imageView) // パン(ドラッグ)の準備 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panAction(_:))) panGesture.delegate = self panGesture.minimumNumberOfTouches = 1 panGesture.maximumNumberOfTouches = 2 imageView.addGestureRecognizer(panGesture) // ピンチイン・アウトの準備 let pinchGetsture = UIPinchGestureRecognizer(target: self, action: #selector(pinchAction(_:))) pinchGetsture.delegate = self imageView.addGestureRecognizer(pinchGetsture) // ダブルタップの準備 let doubleTapGesture = UITapGestureRecognizer(target: self, action:#selector(doubleTapAction(_:))) doubleTapGesture.numberOfTapsRequired = 2 imageView.addGestureRecognizer(doubleTapGesture) }
UIGestureRecognizerDelegate
複数のジェスチャーを登録する場合に必要
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true }
パン(ドラッグ)の処理
画像をドラッグできるようにします。拡大しているときにドラッグした場合に移動量が少なくなってしまうので、CGAffineTransformを一旦デフォルトに戻す処理を入れています
func panAction(gesture: UIPanGestureRecognizer) { // 現在のtransfromを保存 let transform = imageView.transform // imageViewのtransformを初期値に戻す // これを入れないと、拡大時のドラッグの移動量が少なくなってしまう imageView.transform = CGAffineTransformIdentity // 画像をドラッグした量だけ動かす let point: CGPoint = gesture.translationInView(imageView) let movedPoint = CGPointMake(imageView.center.x + point.x, imageView.center.y + point.y) imageView.center = movedPoint // 保存しておいたtransformに戻す imageView.transform = transform // ドラッグで移動した距離をリセット gesture.setTranslation(CGPointZero, inView: imageView) }
ピンチイン・アウトの処理
ピンチした位置を中心としてズーム(イン/アウト)するように、画像の中心位置をずらしています
func pinchAction(gesture: UIPinchGestureRecognizer) { if gesture.state == UIGestureRecognizerState.Began { // ピンチジェスチャー・開始 currentTransform = imageView.transform // ピンチを開始したときの画像の中心点を保存しておく pinchStartImageCenter = imageView.center let touchPoint1 = gesture.locationOfTouch(0, inView: self) let touchPoint2 = gesture.locationOfTouch(1, inView: self) // 指の中間点を求めて保存しておく // UIGestureRecognizerState.Changedで毎回求めた場合、ピンチ状態で片方の指だけ動かしたときに中心点がずれておかしな位置でズームされるため pichCenter = CGPointMake((touchPoint1.x + touchPoint2.x) / 2, (touchPoint1.y + touchPoint2.y) / 2) } else if gesture.state == UIGestureRecognizerState.Changed { // ピンチジェスチャー・変更中 let scale = gesture.scale // ピンチを開始してからの拡大率。差分ではない // ピンチした位置を中心としてズーム(イン/アウト)するように、画像の中心位置をずらす let newCenter = CGPointMake( pinchStartImageCenter.x - ((pichCenter.x - pinchStartImageCenter.x) * scale - (pichCenter.x - pinchStartImageCenter.x)), pinchStartImageCenter.y - ((pichCenter.y - pinchStartImageCenter.y) * scale - (pichCenter.y - pinchStartImageCenter.y))) imageView.center = newCenter imageView.transform = CGAffineTransformConcat(currentTransform, CGAffineTransformMakeScale(scale, scale)) } else if gesture.state == UIGestureRecognizerState.Ended { // ピンチジェスチャー終了 // 現在の拡大率を取得する let currentScale = sqrt(abs(imageView.transform.a * imageView.transform.d - imageView.transform.b * imageView.transform.c)) // 初期サイズより小さい場合は、初期サイズに戻す if currentScale < 1.0 { UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {() -> Void in self.imageView.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2) self.imageView.transform = CGAffineTransformMakeScale(1.0, 1.0) }, completion: {(finished: Bool) -> Void in }) } } }
ダブルタップの処理
func doubleTapAction(gesture: UITapGestureRecognizer) { if gesture.state == UIGestureRecognizerState.Ended { currentTransform = imageView.transform var doubleTapStartCenter = imageView.center var transform: CGAffineTransform! = nil var scale: CGFloat = 2.0 // ダブルタップでは現在のスケールの2倍にする // 現在の拡大率を取得する let currentScale = sqrt(abs(imageView.transform.a * imageView.transform.d - imageView.transform.b * imageView.transform.c)) let tapPoint = gesture.locationInView(self) var newCenter: CGPoint // 拡大済みのサイズがmaxScaleを超えていた場合は、初期サイズに戻す if currentScale * scale > maxScale { scale = 1 transform = CGAffineTransformIdentity newCenter = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2) doubleTapStartCenter = newCenter } else { transform = CGAffineTransformConcat(currentTransform, CGAffineTransformMakeScale(scale, scale)) newCenter = CGPointMake( doubleTapStartCenter.x - ((tapPoint.x - doubleTapStartCenter.x) * scale - (tapPoint.x - doubleTapStartCenter.x)), doubleTapStartCenter.y - ((tapPoint.y - doubleTapStartCenter.y) * scale - (tapPoint.y - doubleTapStartCenter.y))) } // ズーム(イン/アウト)と中心点の移動 UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {() -> Void in self.imageView.center = newCenter self.imageView.transform = transform }, completion: {(finished: Bool) -> Void in }) } }