Glisser-déposer de base sous iOS

93

Je veux avoir une vue dans laquelle il y a des véhicules qui circulent que l'utilisateur peut également glisser-déposer. Quelle est selon vous la meilleure stratégie à grande échelle pour y parvenir? Est-il préférable d'obtenir des événements tactiles à partir des vues représentant les véhicules ou d'une vue plus large? Y a-t-il un paradigme simple que vous avez utilisé pour le glisser-déposer qui vous satisfait? Quels sont les inconvénients des différentes stratégies?

Luke
la source

Réponses:

126

Supposons que vous ayez une UIViewscène avec une image d'arrière-plan et beaucoup vehicles, vous pouvez définir chaque nouveau véhicule comme un UIButton(UIImageView fonctionnera probablement aussi):

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];

Ensuite, vous pouvez déplacer le véhicule où vous le souhaitez, en répondant à l' UIControlEventTouchDragInsideévénement, par exemple:

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

Il est beaucoup plus facile pour un véhicule individuel de gérer ses propres traînées, comparativement à la gestion de la scène dans son ensemble.

ohho
la source
Ça a l'air génial et très simple! Je vais essayer ça.
Luke
10
Cette stratégie entraînerait-elle une interruption du glissement si l'utilisateur faisait glisser le bouton plus rapidement que l'animation mise à jour? Si leur doigt sortait de la boîte, il cesserait de recevoir l'imageMoved: withEvent: calls, correct?
Tim
Comment pouvez-vous savoir si l'image cesse de glisser? Comment sauriez-vous que le doigt a déménagé?
ymutlu
ohho - est-il possible de décaler la "poignée" de glissement d'une image ou d'un bouton afin qu'elle puisse être déplacée par le coin supérieur droit ou supérieur gauche?
LJ Wilson
quand j'essaye d'obtenir une exception - [drag_move_textViewController imageTouch: withEvent:]: sélecteur non reconnu envoyé à l'instance 0x4b4b1c0
iosDev
34

En plus de la réponse d'Ohho, j'ai essayé une implémentation similaire, mais sans problème de glissement "rapide" et de centrage pour faire glisser.

L'initialisation du bouton est ...

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];

et mise en œuvre de la méthode:

- (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;
}

Je ne dis pas que c'est la solution parfaite pour la réponse. Néanmoins, j'ai trouvé que c'était la solution la plus simple pour faire glisser.

JakubKnejzlik
la source
2
Dans votre exemple de code, vous définissez UIControl * control = sender après son utilisation - cela ne devrait-il pas être défini plus tôt?
Peter Johnson
@PeterJohnson: En fait, cela n'a pas d'importance dans ce cas. Il n'y a pas d'utilisation antérieure de cette variable depuis la ligne que vous avez mentionnée :)
JakubKnejzlik
1
Qu'en est-il de CGPoint pPrev = [t previousLocationInView: control]; CGPoint p = [t locationInView: contrôle]; Ces deux lignes précédentes semblent toutes deux faire référence à «contrôle»?
Peter Johnson
Oh mince! Comment puis-je manquer ça? : -O ... J'ai édité la réponse.
JakubKnejzlik
j'ajoute le générateur d'interface de forme de bouton et j'utilise la mise en page automatique pour une connexion Je cache le bouton et le montre à nouveau quand je le fais, il revient à son emplacement d'origine comment éviter ce beviour
Amr Angry
2

J'ai eu un cas où je voudrais faire glisser / déposer des uiviews entre plusieurs autres uiviews. Dans ce cas, j'ai trouvé la meilleure solution pour tracer les événements de panoramique dans une super vue contenant toutes les zones de dépôt et tous les objets de pignon de glissement. La principale raison pour ne PAS ajouter les écouteurs d'événements aux vues déplacées réelles était que j'ai perdu les événements de panoramique en cours dès que j'ai changé la vue supervisée pour la vue déplacée.

Au lieu de cela, j'ai mis en œuvre une solution de glisser-déposer plutôt générique qui pourrait être utilisée sur une ou plusieurs zones de dépôt.

J'ai créé un exemple simple qui peut être vu ici: Faites glisser un uiviews de dépôt entre plusieurs autres uiviews

Si vous décidez d'aller dans l'autre sens, essayez d'utiliser uicontrol au lieu de uibutton. Ils déclarent les méthodes addTarget et sont beaucoup plus faciles à étendre - donc donnent un état et un comportement personnalisés.

Jeyben
la source
1

Je suivrais en fait le glissement sur la vue du véhicule elle-même, plutôt que sur la vue large - à moins qu'il n'y ait une raison particulière de ne pas le faire.

Dans un cas où j'autorise l'utilisateur à placer des éléments en les faisant glisser sur l'écran. Dans ce cas, j'ai expérimenté à la fois la possibilité de faire glisser la vue de dessus et les vues enfants . J'ai trouvé que c'est un code plus propre si vous ajoutez quelques vues "déplaçables" à l'UIView et comment vous pouvez les faire glisser. J'ai utilisé un simple rappel à l'UIView parent pour vérifier si le nouvel emplacement était approprié ou non - afin que je puisse indiquer avec des animations.

Faire glisser la piste de la vue de dessus est aussi bien, je suppose, mais cela le rend légèrement plus compliqué si vous souhaitez ajouter des vues non déplaçables qui interagissent toujours avec l'utilisateur, comme un bouton.

Magnus
la source
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");
}
Balaji G
la source
1

La réponse d'Ohho a bien fonctionné pour moi. Si vous avez besoin de la version swift 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
}
parag
la source
0

Pour Swift 4 Le moyen le plus simple et le plus simple. 😛

Étape 1: Connectez votre bouton du Storyboard à View Controller. Et définir toutes les contraintes de base de mise en page automatique

 @IBOutlet weak var cameraButton: UIButton!

Étape 2: Ajouter un mouvement de panoramique pour votre bouton dans viewDidLoad ()

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

Étape 3:

Ajouter panGestureHandler

@objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {

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

    }

Et maintenant votre action de bouton

@IBAction func ButtonAction(_ sender: Any) {

        print("This is button Action")
    }

entrez la description de l'image ici

Ajouter Voir le résultat ☝️

Anup Gupta
la source