iOSの基本的なドラッグアンドドロップ


93

ユーザーがドラッグアンドドロップすることもできる運転中の車があるビューが欲しいです。これを行うための最良の大規模戦略は何だと思いますか?車両を表すビューから、またはより大きなビューからタッチイベントを取得するのが最善ですか?ドラッグアンドドロップに使用した、満足できる簡単なパラダイムはありますか?異なる戦略の欠点は何ですか?


回答:


126

UIView背景画像と多くのシーンがあるとします。vehicles新しい車両をそれぞれ次のように定義できますUIButton(UIImageViewもおそらく機能します)。

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(imageTouch:withEvent:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:@selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button setImage:[UIImage imageNamed:@"vehicle.png"] forState:UIControlStateNormal];
[self.view addSubview:button];

次に、UIControlEventTouchDragInsideイベントに応答することにより、車両を好きな場所に移動できます。例:

- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
    CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
    UIControl *control = sender;
    control.center = point;
}

シーン全体を管理するよりも、個々の車両が独自のドラッグを処理する方がはるかに簡単です。


素晴らしくてとてもシンプルに見えます!これを試してみます。
ルーク

10
この戦略により、ユーザーが更新されたアニメーションよりも速くボタンをドラッグした場合、ドラッグが中断しますか?彼らの指がボックスの外に出た場合、imageMoved:withEvent:呼び出しの受信が停止しますよね?
Tim

画像のドラッグが停止したかどうかをどのように確認できますか?指が出たことをどのように知っていますか?
ymutlu

ohho-画像またはボタンのドラッグ「ハンドル」をオフセットして、右上または左上隅でドラッグできるようにすることはできますか?
LJウィルソン

例外を取得しようとしているとき-[drag_move_textViewController imageTouch:withEvent:]:認識されないセレクターがインスタンス0x4b4b1c0に送信されました
iosDev

34

ohhoの回答に加えて、同様の実装を試しましたが、「速い」ドラッグとドラッグのセンタリングの問題はありませんでした。

ボタンの初期化は...

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button addTarget:self action:@selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragOutside];
[button setImage:[UIImage imageNamed:@"vehicle.png"] forState:UIControlStateNormal];
[self.view addSubview:button];

およびメソッドの実装:

- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
    UIControl *control = sender;

    UITouch *t = [[event allTouches] anyObject];
    CGPoint pPrev = [t previousLocationInView:control];
    CGPoint p = [t locationInView:control];

    CGPoint center = control.center;
    center.x += p.x - pPrev.x;
    center.y += p.y - pPrev.y;
    control.center = center;
}

これは答えの完璧な解決策だとは言いません。それにもかかわらず、私はこれがドラッグのための最も簡単な解決策だと感じました。


2
サンプルコードでは、UIControl * control = senderを使用した後に定義します。これを先に定義しないでください。
Peter Johnson

@PeterJohnson:実際、この場合は問題になりません。あなたが言及した行以来、この変数の以前の使用法はありません:)
JakubKnejzlik

1
CGPoint pPrev = [t previousLocationInView:control]についてはどうでしょうか。CGPoint p = [t locationInView:control]; これら2つの以前の行はどちらも「コントロール」を参照しているように見えますか?
Peter Johnson

やばい!どうすればそれを逃すことができますか?:-O ...回答を編集しました。
JakubKnejzlik

ボタンフォームインターフェイスビルダーを追加し、一部のログインにautolayoutを使用します。ボタンを非表示にして、再び表示すると、元の場所に戻ります。これを回避するには
Amr Angry

2

他の複数のuiview間でuiviewをドラッグ/ドロップしたい場合がありました。その場合、すべてのドロップゾーンとすべてのドラッグゲーブルオブジェクトを含むスーパービューでパンイベントをトレースするための最良の解決策を見つけました。実際のドラッグされたビューにイベントリスナーを追加しない主な理由は、ドラッグされたビューのスーパービューを変更するとすぐに、進行中のパンイベントが失われたためです。

代わりに、単一または複数のドロップゾーンで使用できるかなり一般的なドラッグドロップソリューションを実装しました。

ここに表示される簡単な例を作成しました:ドロップUiviewを他の複数のUiviewの間にドラッグします

別の方法を選択する場合は、uibuttonの代わりにuicontrolを使用してみてください。これらはaddTargetメソッドを宣言し、拡張がはるかに簡単です-したがって、カスタムの状態と動作を提供します。


1

実際には、大きなビューではなく、車両ビュー自体のドラッグを追跡します-特に理由がない限り。

ユーザーがアイテムを画面上にドラッグして配置できるようにする場合。その場合、トップビュー子ビューの両方をドラッグできるようにしてみました。いくつかの「ドラッグ可能な」ビューをUIViewに追加し、それらをドラッグする方法を処理すると、コードがよりクリーンになることがわかりました。親UIViewへの単純なコールバックを使用して、新しい場所が適切かどうかを確認しました-アニメーションで示すことができました。

持つトップビューの私が推測するドラッグトラックは良いようですが、ボタンなど、まだ相互作用ユーザーと非ドラッグ可能なビューを、追加したい場合には、それより少し厄介になります。


1
-(void)funcAddGesture
{ 

 // DRAG BUTTON
        UIPanGestureRecognizer *panGestureRecognizer;
        panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragButton:)];
        panGestureRecognizer.cancelsTouchesInView = YES;

        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(50, 100, 60, 60);
        [button addTarget:self action:@selector(buttontapEvent) forControlEvents:UIControlEventPrimaryActionTriggered];
        [button setImage:[UIImage imageNamed:@"button_normal.png"] forState:UIControlStateNormal];
        [button addGestureRecognizer:panGestureRecognizer];
        [self.view addSubview:button];
}


- (void)dragButton:(UIPanGestureRecognizer *)recognizer {

    UIButton *button = (UIButton *)recognizer.view;
    CGPoint translation = [recognizer translationInView:button];

    float xPosition = button.center.x;
    float yPosition = button.center.y;
    float buttonCenter = button.frame.size.height/2;

    if (xPosition < buttonCenter)
        xPosition = buttonCenter;
    else if (xPosition > self.view.frame.size.width - buttonCenter)
        xPosition = self.view.frame.size.width - buttonCenter;

    if (yPosition < buttonCenter)
        yPosition = buttonCenter;
    else if (yPosition > self.view.frame.size.height - buttonCenter)
        yPosition = self.view.frame.size.height - buttonCenter;

    button.center = CGPointMake(xPosition + translation.x, yPosition + translation.y);
    [recognizer setTranslation:CGPointZero inView:button];
}


- (void)buttontapEvent {

    NSLog(@"buttontapEvent");
}

1

ohhoの答えは私にはうまくいきました。迅速な2.xバージョンが必要な場合:

override func viewDidLoad() {

    let myButton = UIButton(type: .Custom)
    myButton.setImage(UIImage(named: "vehicle"), forState: .Normal)

    // drag support
    myButton.addTarget(self, action:#selector(imageMoved(_:event:)), forControlEvents:.TouchDragInside)
    myButton.addTarget(self, action:#selector(imageMoved(_:event:)), forControlEvents:.TouchDragOutside)
}

private func imageMoved(sender: AnyObject, event: UIEvent) {
    guard let control = sender as? UIControl else { return }
    guard let touches = event.allTouches() else { return }
    guard let touch = touches.first else { return }

    let prev = touch.previousLocationInView(control)
    let p = touch.locationInView(control)
    var center = control.center
    center.x += p.x - prev.x
    center.y += p.y - prev.y
    control.center = center
}


0

Swift 4の場合簡単で簡単な方法。😛

ステップ1: ストーリーボードからView Controllerにボタンを接続します。すべての基本的なAutoLayout制約を設定します

 @IBOutlet weak var cameraButton: UIButton!

手順2:viewDidLoad()で ボタンのパンジェスチャーを追加する

self.cameraButton.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGestureHandler(panGesture:))))

ステップ3:

panGestureHandlerを追加

@objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {

         let location = recognizer.location(in: view)
        cameraButton.center = location

    }

そして今あなたのボタンアクション

@IBAction func ButtonAction(_ sender: Any) {

        print("This is button Action")
    }

ここに画像の説明を入力してください

追加結果を見る☝️

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.