【Swift】ピンチイン・アウト・ダブルタップで画像を拡大する2(UIScrollView未使用バージョン)

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

【Swift】ピンチイン・アウト・ダブルタップで画像を拡大する1(UIScrollView使用バージョン)
ピンチイン・アウトとダブルタップで画像(UIImageView)を拡大する方法です UIScrollViewにUIImageViewを設置...

今回の実装では、前回の不満点だったダブルタップでのズーム位置のズレが解消されています

前回の不満点

ピンチイン・アウト・ダブルタップで画像を拡大する(UIScrollViewを使用)

今回の実装

ピンチイン・アウト・ダブルタップで画像を拡大する(UIScrollViewを未使用)

仕様はこんな感じです

  • ドラッグで移動する
  • ピンチインで拡大
  • ピンチアウトで縮小
  • ダブルタップでスケールをその時点の2倍にする
  • スケールが5倍以上になった場合は等倍に戻す
  • スケールが等倍以下の場合は等倍に戻す
スポンサーリンク
Google336 記事下

ジェスチャーを登録

画像を配置して、パン、ピンチイン・アウト、ダブルタップのジェスチャーを登録します

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
		})
		
	}
}

関連コンテンツ
ブログランキング
  • にほんブログ村 教育ブログ プログラミング教育へ
  • にほんブログ村 子育てブログ 子供の習い事(教室・業者)へ

  • スポンサーリンク
    Google336 記事下
    Google336 記事下

    シェアする

    • このエントリーをはてなブックマークに追加

    フォローする

    トップへ戻る