IOS 中的基本拖放

我想有一个视图,其中有车辆驾驶周围,用户也可以拖动和下降。你认为最好的大规模策略是什么?最好是从代表车辆的视图中获得触摸事件,还是从更大的视图中获得触摸事件?是否有一个简单的范例,您已经用于拖放,您感到满意?不同策略的缺点是什么?

74692 次浏览

I would actually track dragging on the vehicle view itself, rather than the large view - unless there is a particular reason no to.

In one case where I allow the user to place items by dragging them on the screen. In that case I experimented with both having the top view and the child views draggable. I found it's cleaner code if you add a few "draggable" views to the UIView and handle how to they could be dragged. I used a simple callback to the parent UIView to check if the new location was suitable or not - so I could indicate with animations.

Having the top view track dragging I guess is as good, but that makes it slightly more messy if you would like to add non-draggable views that still interact with the user, such as a button.

Assume you have a UIView scene with a background image and many vehicles, you may define each new vehicle as a UIButton (UIImageView will probably work too):

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

Then you may move the vehicle wherever you want, by responding to the UIControlEventTouchDragInside event, e.g.:

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

It's a lot easier for individual vehicle to handle its own drags, comparing to manage the scene as a whole.

I had a case where I would like to drag/drop uiviews between multiple other uiviews. In that case I found the best solution to trace the pan events in a super view containing all the drop zones and all the drag gable objects. The primary reason for NOT adding the event listeners to the actual dragged views was that I lost the ongoing pan events as soon as I changed superview for the dragged view.

Instead I implemented a rather generic drag drop solution that could be used on single or multiple drop zones.

I have created a simple example that can be seen here: Drag an drop uiviews between multiple other uiviews

If you decide to go the other way, try using uicontrol instead of uibutton. They declare the addTarget methods and are much easier to extend - hence give custom state and behavior.

In addition to ohho's answer I've tried similar implementation, but without problem of "fast" drag and centering to drag.

The button initialization is...

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

and method implementation:

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

I don't say, that this is the perfect solution for the answer. Nevertheless I found this the easiest solution for dragging.

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

ohho's answer worked well for me. In case you need the swift 2.x version:

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
}

For Swift 4 Easy and easiest way. 😛

Step 1: Connect Your Button From Storyboard To View Controller. And Set The all Basic AutoLayout Constraints

 @IBOutlet weak var cameraButton: UIButton!

Step 2: Add Pan Gesture for your button In viewDidLoad()

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

Step 3:

Add panGestureHandler

@objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {


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


}

And Now Your Button Action

@IBAction func ButtonAction(_ sender: Any) {


print("This is button Action")
}

enter image description here

Add See the Result ☝️