2011-01-16 10 views
91

Ich möchte eine Ansicht haben, in der Fahrzeuge herumfahren, die der Benutzer auch ziehen und ablegen kann. Was ist Ihrer Meinung nach die beste groß angelegte Strategie dafür? Ist es am besten, Berührungsereignisse von den Ansichten zu erhalten, die die Fahrzeuge darstellen, oder von der größeren Ansicht? Gibt es ein einfaches Paradigma, das Sie zum Ziehen und Ablegen verwendet haben und mit dem Sie zufrieden sind? Was sind die Nachteile verschiedener Strategien?Basic Drag and Drop in iOS

+0

https://developer.apple.com/ ios/drag-and-drop/ –

Antwort

126

Angenommen, Sie eine UIView Szene mit einem Hintergrundbild haben und viele vehicles, können Sie jedes neue Fahrzeug als UIButton (UIImageView wird wahrscheinlich funktionieren auch) definieren möchten, indem auf die UIControlEventTouchDragInside Ereignis, zB reagiert: Vergleichen

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

es viel einfacher für einzelne Fahrzeug ist seine eigene schleppt zu handhaben, die Szene als Ganzes zu verwalten.

+0

Sieht gut aus und sehr einfach! Ich werde es versuchen. – Luke

+10

Würde diese Strategie dazu führen, dass das Ziehen abgebrochen wird, wenn der Benutzer die Schaltfläche schneller als die Animation aktualisiert hat? Wenn ihr Finger außerhalb der Box ist, würde er das imageMoved nicht mehr empfangen: withEvent: calls, correct? – Tim

+2

Ja, Tim, tut es. – Luke

1

Ich würde tatsächlich ziehen ziehen auf der Fahrzeugansicht selbst statt der großen Ansicht - es sei denn, es gibt einen bestimmten Grund nicht zu.

In einem Fall, wo ich dem Benutzer erlauben, Elemente durch Ziehen auf dem Bildschirm zu platzieren. In diesem Fall experimentierte ich sowohl mit der Draufsicht und die unter Ansichten ziehbar. Ich habe festgestellt, dass es sauberer Code ist, wenn Sie der UIView ein paar "ziehbare" Ansichten hinzufügen und damit umgehen können, wie sie gezogen werden können. Ich habe einen einfachen Callback zum übergeordneten UIView verwendet, um zu prüfen, ob der neue Standort geeignet ist oder nicht - also könnte ich mit Animationen angeben.

Mit der Draufsicht Spur ziehen Ich denke, ist so gut, aber das macht es etwas chaotischer, wenn Sie nicht-ziehbare Ansichten hinzufügen möchten, die immer noch mit dem Benutzer interagieren, wie eine Schaltfläche.

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

Dann können Sie das Fahrzeug bewegen, wohin Sie:

2

Ich hatte einen Fall, in dem ich uiviews zwischen mehreren anderen uiviews ziehen/ablegen wollte. In diesem Fall habe ich die beste Lösung gefunden, um die Pan-Ereignisse in einer Super-Ansicht zu verfolgen, die alle Drop-Zonen und alle Drag Gable-Objekte enthält. Der Hauptgrund für das Hinzufügen der Ereignis-Listener zu den tatsächlichen gezogenen Ansichten war, dass ich die laufenden Schwenkereignisse verlor, sobald ich die Superansicht für die gezogene Ansicht änderte.

Stattdessen implementierte ich eine eher generische Drag-Drop-Lösung, die für einzelne oder mehrere Drop-Zonen verwendet werden konnte.

Ich habe ein einfaches Beispiel erstellt, die hier zu sehen sind: Drag an drop uiviews between multiple other uiviews

Wenn Sie sich entscheiden, den anderen Weg zu gehen, versuchen statt UIButton mit uicontrol. Sie deklarieren die addTarget-Methoden und sind viel einfacher zu erweitern - geben also benutzerdefinierten Status und Verhalten.

34

Zusätzlich zu ohho Antwort habe ich versucht, ähnliche Implementierung, aber ohne Problem von "schnellem" ziehen und zentrieren zu ziehen.

Die Schaltfläche Initialisierung ist ...

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

und Methodenimplementierung:

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

ich sage nicht, dass dies die perfekte Lösung für die Antwort. Nichtsdestotrotz fand ich dies die einfachste Lösung zum Ziehen.

+2

In Ihrem Beispielcode definieren Sie nach der Verwendung UIControl * control = sender - sollte das nicht vorher definiert sein? –

+0

@PeterJohnson: In diesem Fall spielt es keine Rolle. Es gibt keine frühere Verwendung dieser Variable seit der Zeile, die Sie erwähnt haben :) – JakubKnejzlik

+1

Was ist mit CGPoint pPrev = [t previousLocationInView: control]; CGPoint p = [t locationInView: Kontrolle]; Diese beiden früheren Zeilen scheinen sich beide auf "Kontrolle" zu beziehen? –

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"); 
} 
+0

es funktioniert für mich danke –

1

Ohho's Antwort funktionierte gut für mich. Im Fall, dass Sie die schnelle Version 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 
}