質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2927閲覧

UIImageViewの移動範囲を制限したい(touch)

mimamo

総合スコア44

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2018/12/24 05:18

編集2018/12/27 00:38

前提・実現したいこと

今、touchやRotation、PinchすることでUIImageViewを移動、回転、拡大縮小できるようにしています。
次に、移動の制限をしていきたいと思っているのですが、どのようにすればうまくいくのかわかりません。

storyboardに設定したUIview上でだけ可能にしたいと思っています。
下の画像のように、UIImageView(getphoto)が全て画面内に収まる範囲で移動を可能にしたいです。

青線がUIView↓

下のようにUIImageViewが一部もしくは全て消えてしまうのを避けたいです。

サンプルコードやご教授、よろしくお願いいたします。

該当のソースコード

Swift

1 2class ViewController2: UIViewController, UIGestureRecognizerDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate { 3 4 @IBOutlet var rotationRecognizer: UIRotationGestureRecognizer! 5 6 @IBOutlet var pinchRecognizer: UIPinchGestureRecognizer! 7 8//移動、拡大縮小、回転させたいUIImageView 9 @IBOutlet weak var getphoto: UIImageView! 10 let firstImg: UIImage = UIImage(named: "IMG_6632.JPG")! 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 15 //デリゲート先に自分を設定する。 16 rotationRecognizer.delegate = self 17 pinchRecognizer.delegate = self 18 19 //アフィン変換の初期値を設定する。 20 prevEndPinch = getphoto.transform 21 prevEndRotate = getphoto.transform 22 prevPinch = getphoto.transform 23 prevRotate = getphoto.transform 24 25 26 // タッチ操作を enable 27 getphoto.isUserInteractionEnabled = true 28 self.view?.addSubview(getphoto) 29 30 getphoto.image = firstImg 31} 32 33//////////////////////////////////////拡大縮小・回転↓ 34 35//ドラッグ終了時のアフィン変換 36 var prevEndPinch:CGAffineTransform = CGAffineTransform() 37 var prevEndRotate:CGAffineTransform = CGAffineTransform() 38 39 //ドラッグ中の前回アフィン変換 40 var prevPinch:CGAffineTransform = CGAffineTransform() 41 var prevRotate:CGAffineTransform = CGAffineTransform() 42 43 44 @IBAction func pinchPhoto(_ sender: UIPinchGestureRecognizer) { 45 print("pinch!") 46 //前回ドラッグ終了時の拡大縮小を引き継いだアフィン変換を行う。 47 let nowPinch = 48 prevEndPinch.scaledBy(x: sender.scale, y: sender.scale) 49 50 //拡大縮小と回転のアフィン変換を合わせたものをラベルに登録する。 51 getphoto.transform = prevRotate.concatenating(nowPinch) 52 53 //今回の拡大縮小のアフィン変換をクラス変数に保存する。 54 prevPinch = nowPinch 55 56 if(sender.state == UIGestureRecognizerState.ended) { 57 //ドラッグ終了時の拡大終了のアフィン変換をクラス変数に保存する。 58 prevEndPinch = nowPinch 59 } 60 61 } 62 63 @IBAction func rotatePhoto(_ sender: UIRotationGestureRecognizer){ 64 65 print("rotate!") 66 //前回ドラッグ終了時の回転を引き継いだアフィン変換を行う。 67 let nowRotate = prevEndRotate.rotated(by: sender.rotation) 68 69 //拡大縮小と回転のアフィン変換を合わせたものをラベルに登録する。 70 getphoto.transform = prevPinch.concatenating(nowRotate) 71 72 //今回の回転のアフィン変換をクラス変数に保存する。 73 prevRotate = nowRotate 74 75 if(sender.state == UIGestureRecognizerState.ended) { 76 //ドラッグ終了時の回転のアフィン変換をクラス変数に保存する。 77 prevEndRotate = nowRotate 78 } 79 80 } 81 82//////////////////////////////////////画像の移動↓ 83 84 //タッチしたビューの中心とタッチした場所の座標のズレを保持する変数 85 var gapX:CGFloat = 0.0 // x座標 86 var gapY:CGFloat = 0.0 // y座標 87 88 //UIImageのサイズ取得 89 let photoWidth = self.getphoto.image?.size.width 90 let photoHeight = self.getphoto.image?.size.height 91 92 93 // タッチした位置で最初に見つかったところにあるビューを取得してしまおうという魂胆 94 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 95 // 最初にタッチした指のみ取得 96 if let touch = touches.first { 97 print("touchbegin!") 98 // タッチしたビューをviewプロパティで取得する 99 if let touchedView = touch.view { 100 // tag1のものを動かす 101 if touchedView.tag == 1 { 102 // タッチした場所とタッチしたビューの中心座標がどうずれているか? 103 gapX = touch.location(in: view).x - touchedView.center.x 104 gapY = touch.location(in: view).y - touchedView.center.y 105 // 例えば、タッチしたビューの中心のxが50、タッチした場所のxが60→中心から10ずれ 106 // この場合、指を100に持って行ったらビューの中心は90にしたい 107 // ビューの中心90 = 持って行った場所100 - ずれ10 108 touchedView.center = CGPoint(x: touch.location(in: view).x - gapX, y: touch.location(in: view).y - gapY) 109 } 110 } 111 } 112 } 113 114 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 115 // touchesBeganと同じ処理だが、gapXとgapYはタッチ中で同じものを使い続ける 116 // 最初にタッチした指のみ取得 117 if let touch = touches.first { 118 // タッチしたビューをviewプロパティで取得する 119 if let touchedView = touch.view { 120 // tag1のものを動かす 121 if touchedView.tag == 1 { 122 // gapX,gapYの取得は行わない 123 touchedView.center = CGPoint(x: touch.location(in: view).x - gapX, y: touch.location(in: view).y - gapY) 124 } 125 } 126 } 127 } 128 129 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 130 // gapXとgapYの初期化 131 gapX = 0.0 132 gapY = 0.0 133 } 134 135 override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { 136 // touchesEndedと同じ処理 137 self.touchesEnded(touches, with: event) 138 } 139 140 141 //リコグナイザーの同時検知を許可するメソッド 142 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 143 return true 144 } 145 146override func didReceiveMemoryWarning() { 147 super.didReceiveMemoryWarning() 148 // Dispose of any resources that can be recreated. 149} 150 151} 152

試したこと

UIImageView(getphoto)の大きさと中心座標と移動可能にしたい範囲を組み合わせてコードを組めばいいのかなと思ったのですが、実際にどのようにコーディングすればいいのかわかりません。

###エラー

UIImageViewの大きさを知りたくてコーディングしてみたのですがエラーが出てしまいました。

Swift

1//UIImageView(getphoto)のサイズ取得 2 let photoWidth = self.getphoto.image?.size.width 3 let photoHeight = self.getphoto.image?.size.height

エラー

Value of type '(ViewController2) -> () -> (ViewController2)' has no member 'getphoto'

参考資料

補足情報(FW/ツールのバージョンなど)

Swiftのversionは4.1.2
Xcodeのversionは9.4.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takabosoft

2018/12/24 06:23

「UIImageViewの大きさを知りたくてコーディングしてみたのですがエラーが出てしまいました。 」とありますが、どこにそのコードを記述してエラーが出たのかを質問欄に載せてください。
mimamo

2018/12/24 17:47

コードに入ってなかったですね...失礼しました。 訂正しておきました。
guest

回答1

0

ベストアンサー

//UIImageView(getphoto)のサイズ取得

let photoWidth = self.getphoto.image?.size.width let photoHeight = self.getphoto.image?.size.height

上記をメンバーの「定数」として定義していますが、これでは
「クラス初期化の段階で処理を一度だけ実行」するので、
「現在のサイズを取得したい」という希望通りにはうごきません。

やるのであれば、関数の中で動的に値を見てください。
また、transformなどで拡大縮小した結果のサイズを知りたいのであれば
getphoto.image?.size.widthではなくgetphoto.frameを使ってください。

あとは「viewからgetphoto.frameの半分サイズだけInsetした矩形」の中にtouchedView.centerが収まるように制御すればよいです。

投稿2018/12/25 00:14

takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mimamo

2018/12/25 04:42

丁寧なご説明ありがとうございます! ひとつお伺いしたいことがあります。 >>「viewからgetphoto.frameの半分サイズだけInsetした矩形」の中に >>touchedView.centerが収まるように制御すればよい というのは、「viewの各辺からgetphoto.frameの半分サイズだけ引いたUIView」を作り、touchedView.centerがそのUIViewに収まるようにすればよいのでしょうか。
takabosoft

2018/12/25 05:45

違います。view.boundsの各辺からgetphoto.frameの半分サイズだけ引いたCGRectを作り、touchedView.centerがそのCGRectに収まるようにすればよいです。
mimamo

2018/12/26 02:33

お返事遅くなりました。ありがとうございます!  以下のようにコーディングしてみたのですが、この後どのようにコーディングすべきかわかりませんでした。 ``` //移動範囲の制限 func IdouSeigen() { // UIView(canvasView)のサイズを取得 let viewWidth: CGFloat = canvasView.bounds.width //幅 let viewHeight: CGFloat = canvasView.bounds.height //高さ //UIImageView(getphoto)のサイズ取得 let photoWidth = self.getphoto.frame.width let photoHeight = self.getphoto.frame.height let rect = CGRect(x: photoWidth/2 ,y: photoHeight/2 ,width: viewWidth-photoWidth/2 ,height: viewHeight-photoHeight/2) } ```
takabosoft

2018/12/26 02:44

widthとheightは割る2しなくて良いです(rightやbottomなら割る2で良いんですが、この場合widthとheightなのでx, yでずれた分も差し引かないといけないです)。 let rect = CGRect(x: photoWidth/2 ,y: photoHeight/2 ,width: viewWidth-photoWidth ,height: viewHeight-photoHeight) ちなみにlet rect = canvasView.bounds.insetBy(dx: self.getphoto.frame.width / 2, dy: canvasView.bounds.height / 2)などと一発で書くこともできます。 あとは、touchedView.centerを移動させた後、 center.xの値がrect.minX以下の場合はrect.minXに、rect.maxX以上の場合はrect.maxXになるようにコードを組んでください。 同様にcenter.yについてもやってもらえればOKです。
mimamo

2018/12/26 03:51

図に書いてみたら2で割る必要がないのがわかりました。 一文で書くこともできるのですね! xに関しては移動の制限ができるようになりました! ですが、y軸方向に動かすことができなくなってしまいました。 ``` override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { // touchesBeganと同じ処理だが、gapXとgapYはタッチ中で同じものを使い続ける //矩形の設定 let rect = canvasView.bounds.insetBy(dx: self.getphoto.frame.width / 2, dy: canvasView.bounds.height / 2) // 最初にタッチした指のみ取得 if let touch = touches.first { // タッチしたビューをviewプロパティで取得する if let touchedView = touch.view { // tag1のものを動かす if touchedView.tag == 1 { // gapX,gapYの取得は行わない touchedView.center = CGPoint(x: touch.location(in: view).x - gapX, y: touch.location(in: view).y - gapY) if touchedView.center.x <= rect.minX{ touchedView.center.x = rect.minX } if touchedView.center.x >= rect.maxX{ touchedView.center.x = rect.maxX } if touchedView.center.y <= rect.minY{ touchedView.center.y = rect.minY } if touchedView.center.y >= rect.maxY{ touchedView.center.y = rect.maxY } } } } } ```
takabosoft

2018/12/26 07:22

ああ、すみません、こちらのミスです。 let rect = canvasView.bounds.insetBy(dx: self.getphoto.frame.width / 2, dy: self.getphoto.frame.height / 2) ※dyの方が間違ってました こういう原因を見つけるために座標などをprintして動かしてみるとすぐにおかしな場所が判りますよ。
mimamo

2018/12/27 00:32

できました!  なるほど、今度からprintして確認していくようにしようと思います。 丁寧なご回答ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問