iOSアプリケーションでのドラッグアンドドロップ機能の実装

インターフェイス要素を指でドラッグアンドドロップすることは、ジェスチャコントロールの非常に自然な部分になっているため、まれなアプリケーションではそれがなくても実行できます。 ただし、このような関数をコードに適切に登録することは、必ずしも簡単な作業ではありません。 パートナー企業のMusic Breathは、Objective-Cでのドラッグアンドドロップの技術的な実装の特定の側面について読者に説明します。



「現在取り組んでいるプロジェクトの1つは、 ソングライター-歌詞メモパッドです。ミュージシャン向けのノートブックで、いつでもアイデア、良いライン、お気に入りのコードを入力できます。 後者の場合、画像をアプリケーションに挿入してドラッグする機能を実装する必要がありました。この機能は、従来の方法である「ドラッグ&ドロップ」を使用して実行することにしました。 今日は、それをどのように実装し、UnityからNativeにアプリケーションを移行したか、そしてそのプロセスで遭遇した困難を説明します。









もちろん、既成のソリューションの検索から始めました。多くの記事を読み、Googleで数日間過ごし、この機能を実装するための多くのオプションを試しました。 ただし、それらはすべて、オブジェクトをUIViewからUIScrollViewまたはUIViewからUIViewに転送するために作成されました。 当時のタスクには、UIScrollViewからUIScrollViewへの移動が含まれていました(後に、UIScrollViewからUIScrollViewにあるUIViewへの転送が行われることが判明しました)。 それはそれほど単純ではないことが判明しました。多くの問題に直面しましたが、その問題については以下で説明します。



ドラッグ&ドロップの問題



ドラッグ&ドロップを実装するために、UIPanGestureRecognizerを選択しました。 この方法の難しさは次のとおりです。



  1. UIPanGestureRecognizerは、UIScrollViewイベントをキャプチャします。
  2. オブジェクトを転送するのではなく、コピーする必要がありました。 厳密に言えば、この問題はドラッグ&ドロップに直接関係するものではありませんが、実装プロセスで発生しました。
  3. 座標系をあるUIScrollViewから別のUIScrollViewに変換する必要があります。


パラグラフ2と3は組み合わせることができます。 したがって、問題の両方のブロックをより詳細に分析します。



1. UIPanGestureRecognizerはUIScrollViewイベントをキャプチャします。



問題の本質



UIImageViewの転送元のUIScrollViewには、画面に表示されるよりも多くの要素があり(つまり、contentSizeがフレームよりも大きい)、UIPanGestureRecognizerはUIScrollViewイベントをキャプチャするため、他の要素に移動することはできません- UIPanGestureRecognizerは常に呼び出されます。









解決策



この問題を解決するために、stackoverflowのオープンスペースで見つかったUIPanGestureRecognizerのカテゴリを使用しました。 UIPanGestureRecognizerの方向を設定するために-要素を上下にのみ移動させるために必要でした。



コード内での表示:



- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {   [super touchesMoved:touches withEvent:event];   if (self.state == UIGestureRecognizerStateFailed) return;   CGPoint nowPoint = [[touches anyObject] locationInView:self.view];   CGPoint prevPoint = [[touches anyObject] previousLocationInView:self.view];   _moveX += prevPoint.x - nowPoint.x;   _moveY += prevPoint.y - nowPoint.y;   if (!_drag) {     if (abs(_moveX) > kDirectionPanThreshold) {       if (_direction == DirectionPangestureRecognizerVertical) {         self.state = UIGestureRecognizerStateFailed;       }else {         _drag = YES;       }     }else if (abs(_moveY) > kDirectionPanThreshold) {       if (_direction == DirectionPanGestureRecognizerHorizontal) {         self.state = UIGestureRecognizerStateFailed;       }else {         _drag = YES;       }     }   } }
      
      





これで、UIPanGestureRecognizerに特定の方向を設定できます。 このように:



  for (int i =0; i < _scrollChords.imageArray.count; i++)  {    DirectionPanGestureRecognizer *panRecg = [[DirectionPanGestureRecognizer alloc]initWithTarget:self action:@selector(labelDragged:)];    panRecg.direction = DirectionPangestureRecognizerVertical;        [[_scrollChords.imageArray objectAtIndex:i] addGestureRecognizer:panRecg];     }
      
      





ここで、panRecg.direction = DirectionPangestureRecognizerVerticalは、ドラッグ「&」ドロップの方向を定義します。











UIPanGestureRecognizerについて少し:



UIPanGestureRecognizerには、オブジェクトを移動するための3つのイベントがあります。



  1. UIGestureRecognizerStateBegan-オブジェクトにテープを貼り付けたときに、動きが始まるとイベントが返されます。
  2. UIGestureRecognizerStateChanged-オブジェクトが移動しているときにイベントが返されます。
  3. UIGestureRecognizerStateEnded-イベントは、移動が完了すると、つまり、指をすでに離したときに返されます。


オブジェクトの転送を実行するために、すべてのイベントをキャッチします。



2)あるUIScrollViewから別のUIScrollViewへの座標系の変換。 オブジェクトをコピー



問題の本質



私たちの場合、Drag '&' Dropの実装には、座標系の再計算が必要でした。 難しさは、UIScrollView内では、座標が静的ではないことです。上、右、左、下にスクロールすると、座標が変化します。



解決策



まず、UIImageViewを移動していること、および特定のUIImageViewを移動していること、つまり、その座標、幅と高さ、および画像自体(UIImage)に関する情報が必要であると判断する必要がありました。 そして、self.viewでUIImageViewが配置されている中心を決定する必要がありました。



 UIImageView *imageView=(UIImageView *)[recognizer view];  CGPoint newCenter = [recognizer translationInView:self.view];
      
      





この手順は、各UIPanGestureRecognizerイベントの前に実行されます。



オブジェクトを移動するだけでなく、一方のUIScrollViewに表示されるようにもう一方から消えないようにコピーしたことを思い出してください。 さらに、移動プロセスはユーザーに表示される必要があります。



すべてのUIPanGestureRecognizerイベントを検討してください。



2.1。 UIGestureRecognizerStateBegan



 if (recognizer.state==UIGestureRecognizerStateBegan)  {             viewImage = [[UIImageView alloc] init];    CGRect rect = [self.view convertRect:[imageView frame] fromView:[imageView superview]];    viewImage.frame = CGRectMake(rect.origin.x, rect.origin.y, 28, 28);    viewImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@copy",imageView.accessibilityLabel]];    viewImage.contentMode = UIViewContentModeScaleAspectFill;    viewImage.userInteractionEnabled = YES;        [self.view addSubview:viewImage];        beginX = imageView.center.x;    beginY = imageView.center.y; }
      
      





ここでの魔法は、self.viewに画像を作成することです。 しかし、これはほんの始まりに過ぎません。 次に、UIImageViewの座標を再計算し、self.viewに移動して、移動を開始する正確な場所に追加する必要があります。 同じ場所に表示されるので、ユーザーはこの特定の画像をドラッグしているような錯覚を保持できます。



コード全体をいくつかの部分に分割します。



a)変換



CGRect rect = [self.view convertRect:[imageViewフレーム] fromView:[imageView superview]];



移動するオブジェクトの寸法をスーパービューからself.viewに変換し、self.viewで座標を取得します。



 viewImage.frame = CGRectMake(rect.origin.x, rect.origin.y, 28, 28);    viewImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@copy",imageView.accessibilityLabel]];
      
      





次に、self.viewに追加する新しいUIImageViewにフレームを設定します。 同時に、サイズが厳密に定義されることを忘れないため、静的な値、28x28を使用します。



b)画像タスク



もう1つの複雑な点は、単に画像の名前を取得できないことです。UIImageをドラッグしているものとは異なるように移動する必要があります(異なる色、丸いエッジなど)。 これを行うには、UIImageViewを使用して配列を構築するときに、各accessibilityLabelを割り当てて、名前を簡単に取得できるようにします。



     viewImage.contentMode = UIViewContentModeScaleAspectFill;    viewImage.userInteractionEnabled = YES;        [self.view addSubview:viewImage];
      
      













c)必要な段落を追加する



ここでの主なことは、userInteractionEnabled = YESを割り当てることを忘れないことです。そうしないと、UIImageVIewをタップしたときにイベントが認識されません。 UIImageViewを使用して配列を作成したときに、最初にこれを実装しました。 その後、将来、別のUIScrollViewに追加されたものを移動できるため、同じことを行いました。



 viewImage.userInteractionEnabled = YES;
      
      





d)開始点の設定



そして最後に、どのポイントから移動を開始するかを知る必要があります。



 beginX = imageView.center.x; beginY = imageView.center.y;
      
      





これが新しい施設の中心です。 imageViewを使用します。この方法では、要素の移動を開始すると、指のすぐ下になります。 viewImage.centerを使用した場合、移動したオブジェクトは左上に移動します(おそらくこれは改良ではなく、よりシンプルになりますが、将来的には単純に座標を再計算します)。



それで、運動を始めるために必要なことはすべて、私たちはしました。 これでさらに先へ進むことができます。



2.2。 UIGestureRecognizerStateChanged



次に、移動プロセス自体を実装する必要があります。それにより、ユーザーの前で発生します。 これを行うには、UIGestureRecognizerStateChangedイベントを処理するときに、次のことを行います。



  newCenter.x = ( newCenter.x + beginX ); newCenter.y = ( newCenter.y + beginY ); newCenter = [self.view convertPoint:newCenter fromView:imageView.superview]; viewImage.center = newCenter;
      
      





ここでは、座標系の再計算で魔法のトリックが繰り返されます。 出口には、指の直後に移動するオブジェクトがあります。画像はどこにも移動せず、パスの各段階で、本来あるべき場所に移動します。









ここで最も難しいのは、最後に表示したい場所にオブジェクトを追加することです。 問題を複雑にするために、UIScrollViewだけでなく、内部にあるUIViewに移動しています。



すべてはUIGestureRecognizerStateEndedイベントで発生します。



2.3。 UIGestureRecognizerStateEnded



まず、2つのCGRect'aを見つけます。移動した画像とUIViewで、そこに挿入します。



     CGRect rect = [self.view convertRect:[viewImage frame] fromView:[viewImage superview]]; CGRect rectNewView = [self.view convertRect:[[_customfield.viewArray objectAtIndex:j] frame] fromView:[[_customfield.viewArray objectAtIndex:j] superview]];
      
      





指を離すと(つまり、移動が終了した時点で)、viewImageを変換し、同様に挿入する場所を変換します。 実際、ここではパターンに従って動作します。以前はUIScrollViewに挿入していましたが、今度はUIScrollViewにあるUIViewに挿入します。



2つの変換されたCGRectを受け取った後、あるべき場所に到達したかどうかを確認できます。



 if (CGRectIntersectsRect(rect,rectNewView)) { } else { [viewImage removeFromSuperview]; }
      
      





そうでない場合は、作成したUIImageViewを削除してself.viewから移動します。









すべてがうまくいった場合は、次の手順を実行します。



 newCenter = [[_customText.viewArray objectAtIndex:j] convertPoint:newCenter fromView:viewImage.superview]; viewImage.center = CGPointMake(newCenter.x,[[_customText.viewArray objectAtIndex:j] frame].size.height/2);        [[_customText.viewArray objectAtIndex:j] addSubview:viewImage];
      
      





ここで、オブジェクトを移動する先のUIView内のポイントを見つけます。

次に、UIImageVewをY軸に沿って中央に明確に配置する必要があります。このため、新しい座標を設定します。 何が起こったのかを見ると、古いUIScrollViewのままで、写真が望みの場所に複製されていることがわかります。



これで今日のブラックマジックのドラッグ「&」ドロップは終了します。









要約すると、全体の難しさは、何をどこで変換するかを理解することでした。 おそらく、時間の経過とともに、より単純なアルゴリズムを開発することになるでしょうが、今のところ、すべてが機能し、意図したとおりにある場所から別の場所に美しくコピーされることを嬉しく思います。」



All Articles