都道府県穴埋めゲーム

iPhone アプリ作成に関してですが
UIButtonの画像付きボタンをドラッグさせた際
指定した座標を通過もしくはその座標の点の上にボタンが来たらアクションを実行
ということは可能でしょうか?
また、座標ではなく、表示されているUIImageもしくはUIButtonの上を通過したら実行
という処理も可能でしょうか?
作成しているアプリでは、通過する座標は同じなのですが表示されているUIButtonがその前の処理で異なるので
個人的には後者の処理を行いたいです

A 回答 (7件)

> 私が思い当ったコードは


> CGPoint pt = [[touches anyObject] locationInView:self.superview];
> self.center = pt;
> で、タッチした際UIImageViewが少し下にずれるようなことがたまにあったので他によい方法はないのか

それは、タッチした場所と、Viewのセンター位置の差分を、調節しないと、そういうことになるでしょうね。いい方を変えると、調節すれば、なんの問題もありませんね。いちいち他人にアドバイスを仰ぐようなことではないと思います。

私だったら、どういうコードを書くかということですが、私はUITouchではなく、UIPanGestureRecognizerを使います。
ジェスチャーのStateが「UIGestureRecognizerStateBegan」(画面をタッチして、移動を始めた瞬間)のときに、「setTranslation:inView:」でViewのcenterを登録し、ジェスチャーのState「UIGestureRecognizerStateChanged」(タッチを移動している間」、「translationInView:」で取得されるCGPointに、Viewのcenterをセットしてやると、Viewがぴったり指に追従します。

UITouchを使うケースで、もうすこしアドバイスすることが残っているとしたら、ひとつは「previousLocationInView:」というメソッドを活用することです。
「touchesMoved:withEvent:」メソッドは、タッチを移動しているあいだ、なんども繰り返して呼び出されると、前に説明しましたが、前回呼び出し時のタッチ位置が「previousLocationInView:」で取得できるので、タッチ移動量を計算することが可能になります。

サンプルコード:
記述場所:UIViewControllerのサブクラス
targetObj:指で動かすオブジェクト。UIViewのサブクラス。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
~ // タッチ開始時の処理
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if ([touch.view isEqual: targetObj]) { // targetObjをタッチしたのなら
CGPoint pt = targetObj.center;
CGPoint prevPt = [touch previousLocationInView: self.view];
CGPoint newPt = [touch locationInView: self.view];
float gapX = newPt.x - prevPt.x; // タッチ移動量を計算
float gapY = newPt.y - prevPt.y;
targetObj.center = CGPointMake(pt.x + gapX, pt.y + gapY);
}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
~ // タッチ終了時の処理
}

タッチイベントを処理するメソッドを、UIViewController(のサブクラス)に記述している点に注目してください。それがもうひとつのアドバイスです。UIViewControllerは、UIResponderを継承しているので、タッチイベントの処理が可能です。
「レスポンダチェーン」(Responder Chain)という言葉を覚えてください。いかにもオブジェクト指向的なメカニズムです。
画面を指でタッチすると、タッチイベントが発生しますが、タッチした場所にあるViewがまずタッチイベントを受け取ります。もしそのViewにタッチイベントの処理が用意されていなかったら、そのViewはイベントをSuperviewに譲り渡します。Superviewにイベント処理が用意されていたら、代わって処理を行いますが、SuperviewはみずからのSuperviewにタッチイベントを譲り渡します。このようにイベントは次々にView間を渡っていき、UIWindowに渡され、最終的にUIApplicationに渡されます。どこでもイベントに対する処理がなければ、けっきょくなにも起きずにイベントは消滅します。このイベント受け渡しの経路をレスポンダチェーンと呼びます。経路にあるUIResponderサブクラスなら、プログラム上つごうのいい場所で、イベント処理を記述することができます。
Viewがコントローラ(UIViewController)を持っている場合、タッチイベントは、Superviewに渡される前に、コントローラに渡されます。サンプルコードはこの受け渡しの規則を利用して、UIViewControllerのサブクラスにタッチイベントの処理を記述しています。
タッチイベントを最初に受け取ったViewは、UITouchのプロパティ「view」で取得することができます。
    • good
    • 0
この回答へのお礼

詳しい説明ありがとうございます
最初の質問から主旨少しズレてしまいすみませんでした
それでも最後までつきあっていただき感謝します
次はUIPanGestureRecognizerで作成をしてみようと思います
たびたび質問して来ると思いますが機会があればまたよろしくお願いします

お礼日時:2011/08/08 21:23

> これでよろしかったでしょうか?



あなたの書いたプログラム全体を把握できないので、その質問には答えられません。しかし、その書き換えであなたの意図どおりの動作になったのなら、正しいと判断していいのではありませんか?
いちど、そのプログラムから離れて、1週間ほど忘れてしまうことです。そのあと、そのプログラムを読み返せば、客観的な判断ができるようになるでしょう。人間は本質とその構造として、進歩し成長するようになっている。人間は自分自身を客観的に把握する能力を有する。このふたつのことを信じる価値があります。

この回答への補足

そうですね。
できればharawoさんが考えたコードをお聞きしたくて
よろしかったですか?と尋ねました
私が思い当ったコードは
CGPoint pt = [[touches anyObject] locationInView:self.superview];
self.center = pt;
で、タッチした際UIImageViewが少し下にずれるようなことがたまにあったので他によい方法はないのか
harawoさんならどのように書くかというのが知りたくて捕捉させていただきました

補足日時:2011/08/07 22:53
    • good
    • 0

> このようにしたところ当然UIImageは動きませんがマウスでAのUIButtonの位置まで持っていくと LogにINと表示されました


> 原因は下から3行目と4行目の箇所ですがなぜいけないのか
> どうすればいいのか、というのが分かりません

「UIImage」は動きませんよ。UIViewのサブクラスではありませんからね。UIImageは、オフスクリーンバッファという考え方をクラス化したものです。プログラムが動くたび、画面を書き換えていると、人間の目にはちらつきに見えて、不快になったり疲れさせたりするので、画面表示データと、プログラムの対象のイメージデータを別にし、ある程度書き換えが完了したら、一気に画面表示データと置き換えるというテクニックで使われます。
あなたがいいたいのは「UIImageView」ですね?かんたんにUIImageを画面表示させるための、UIViewのサブクラスです。UIImageとUIImageViewを、意図して書き分けできずに、混同してしまうのは、UIKitというフレームワークを、理解できていないからにほかならないでしょう。

UIViewサブクラスの位置は「マウス」では動かせませんよね?iOSシミュレータ上では、たしかにマウスで動かしますが、実機では指のタッチで動かしますよね?さきほど「ドラッグ」という言葉の使い方を例にとって、GUIを意識したプログラムをするようアドバイスしたつもりなのですが、こちらの意図を理解していたら、こういう混同もしないのでしょうに。

UIViewの位置情報は、「center」という、読み書きのできる便利なプロパティが用意されているのですから、「uiviewsubclass.frame.origin.x」などと、いちいち記述せずに、centerを使いましょう。

UIViewサブクラスのインスタンスuiviewsubclassの位置を、CGPoint「distance」の分だけ動かすには、こう書きます。

uiviewsubclass.center = CGPointMake(uiviewsubclass.center.x + distance.x, uiviewsubclass.center.y + distance.y);

さて、本題の質問に対してですが、取得する座標の基準、メソッド「locationInView:」の引数となるViewは、「self」で正しいんですか?私は違うと思いますが?

この回答への補足

間違った語句が多くてすみません
touchesMoved内を
CGPoint pt = [[touches anyObject] locationInView:self.superview];
self.center = pt;
このように変えたらUIImageViewを動かすことができした。
これでよろしかったでしょうか?

補足日時:2011/08/07 13:32
    • good
    • 0

> touchesBegan:にコードは書き込みました



touchesBegan:withEvent:メソッドは、指を画面にタッチした瞬間に、1回だけ呼ばれます。タッチした指を、画面から離さずに移動する間の処理は、「touchesMoved:withEvent:」メソッドに記述します。ここにNSLog()を書くと、指を動かしている間、連続して繰り返しこのメソッドが呼ばれることがわかります。
タッチイベントのメソッドは、「touchesBegan:withEvent:」「touchesMoved:withEvent:」「touchesEnded:withEvent:」「touchesCancelled:withEvent:」の4つがセットと考え、処理を行ってください。ひとつだけ取り出して処理しても、期待どおりにならないことのほうが多いでしょう。ちなみに、touchesCancelled:withEvent:メソッドは、iPhoneで途中電話がかかってきたなど、外的要因でタッチイベントが中断されたときに呼び出されます。

> if (CGRectContainsPoint(A.frame, pt)){
>  NSLog(@"TEST");
> }
> と書き込んでみましたが表示されませんでした

条件に一致しないときも、なにかしら出力しなければ、ちゃんと判断できませんよ。

if (CGRectContainsPoint(A.frame, pt)){
 NSLog(@"IN");
} else {
NSLog(@"OUT");
}

この回答への補足

原因が判明しました
elseも書き行ったところtouchesMoved内のに問題があることが判明しました(少し長くなりますが)

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
startLocation = [[touches anyObject] locationInView:self];
[[self superview] bringSubviewToFront:self];
}

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView:self];
CGRect frame = [self frame];
if (CGRectContainsPoint(A.frame, pt)){
NSLog(@"IN");
}else{
NSLog(@"OUT");;
}
//frame.origin.x += pt.x - startLocation.x;
// frame.origin.y += pt.y - startLocation.y;
[self setFrame:frame];
}
このようにしたところ当然UIImageは動きませんがマウスでAのUIButtonの位置まで持っていくと LogにINと表示されました
原因は下から3行目と4行目の箇所ですがなぜいけないのか
どうすればいいのか、というのが分かりません
できればもう少しアドバイスをいただきたいです

補足日時:2011/08/07 00:01
    • good
    • 0

> ドラッグのclassに


> CGPoint pt = [[touches anyObject] locationInView:self];
> CGRect frame = [self frame];
> if (CGRectContainsPoint(A.frame, pt)){
> [処理];
> }
> とやってみたのですが処理は実行されませんでした

「なにを」は書かれていても、「どこに」(メソッド)が書かれていないので、なんともコメントのしようがありません。
処理のコードを書いていても、それを呼び出すイベントが発生しなければ、また発生したイベントを、メソッドが受け取らなければ、処理は実行されません。
イベントが発生しているか、あるいは発生したイベントを、メソッドが受け取っているかを調べるには、NSLog()を使った、簡易デバッグが有効でしょう。
そして、冷静に、客観的にプロジェクトを観察すれば、タッチイベントがどのオブジェクト(UIResponderのサブクラス)で受け止められているか、調べるのは容易なはずです。いちど自分のプロジェクトを俯瞰してみてはいかがですか?
(まさか、「touchesMoved:withEvent:」メソッドに書いていないなんてことはありませんよね?)

※「ドラッグ」という言葉は、厳密な定義に従えば、マウスボタンを押したままの状態で、マウスを動かす行為を指します。iPhoneのようなタッチパネル式のインターフェイスでは、使うべき用語とはいえません。Appleの用語では、Touch Moveや、Pan(UIPanGestureRecognizer)が使われます。

この回答への補足

説明が足りませんでしたね
(ドラッグの件も気をつけます)
touchesBegan:にコードは書き込みました
NSLog(@"TEST");とtouchesBegan:の処理内に書き込んだところ表示はされたのでこちらは問題ないのですが
if (CGRectContainsPoint(A.frame, pt)){
 NSLog(@"TEST");
}
と書き込んでみましたが表示されませんでした
発生自体はしているはずなのですがメソッドが受け取ってくれていないのだと思います。
TouchMoveの処理がDragView.hに
通過させたいUIButtonの処理がMainViewController.hに書かれているのでController.h内のIBOutlet UIButtonがしっかり認識されてないのが原因でしょうか?

補足日時:2011/08/06 09:53
    • good
    • 0

> どういったクラス?を使用すればよいのかなどのアドバイスをいただきたいですね



「UIButtonの画像付きボタンをドラッグ」するのに、UITouchクラスか、UIGestureRecognizerクラスを利用していると思いますが、それらどちらかのメソッドで処理するのが、自然な流れではないでしょうか?

参考:
bool CGRectContainsPoint (
CGRect rect,
CGPoint point
);
ある座標(タッチしているポイント)が、ViewなどのCGRectの内側にあるのか、調べるC言語の関数です。これで判別できます。

> (ちなみにプログラムのサンプルを書いてもらうにはおいくらですか?)

公共の掲示板を使って、営利行為をしたら、まずいですよ。冗談でその質問をなさっているのだと思いますが、こちらの言葉も本気で「金をくれ」といっているのではないことを、誤解しないでください。

この回答への補足

DragView.mを作成しドラッグ用のUIButtonを作成し
MainViewController.mにAというUIButtonを作成
ドラッグのclassに
CGPoint pt = [[touches anyObject] locationInView:self];
CGRect frame = [self frame];
if (CGRectContainsPoint(A.frame, pt)){
[処理];
}
とやってみたのですが処理は実行されませんでした
.hを読み込ませたり、DragView.hにもUIButtonのAを作成してInterfaceBuilderでコネクトさせてみたりといろいろやってみましたがうまくいきませんでした。
(上記のコードだけでは決定的な原因がわかりづらいですね)
できればもう少しアドバイスをお願いしたいです

>誤解しないでください
こちらも冗談のつもりで()を付けて書いたので気にしないでください

補足日時:2011/08/05 15:56
    • good
    • 0

Yes or Noの二択回答を求めていらっしゃるなら、回答は「Yes」です。



もし、具体的にそういうプログラムを書いてくれという要求でしたら、お答えしません。(お金がもらえるなら、いくらでもプログラムを書きますけどね。)

ヒントなり、方向性をアドバイスして欲しいということでしょうか?

この回答への補足

返答ありがとうございます
そうですね
どういったクラス?を使用すればよいのかなどのアドバイスをいただきたいですね
教えていただけたらこちらでも調べますし、うまくいかない点があったらまた新しく質問を投稿させてもらいます
(ちなみにプログラムのサンプルを書いてもらうにはおいくらですか?)

補足日時:2011/08/05 11:40
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!