简体   繁体   English

iOS中的基本拖放功能

[英]Basic Drag and Drop in iOS

I want to have a view in which there are vehicles driving around that the user can also drag and drop. 我希望有一个视图,其中有车辆行驶,用户也可以拖放。 What do you think is the best large-scale strategy for doing this? 您认为这样做的最佳大规模战略是什么? Is it best to get touch events from the views representing the vehicles, or from the larger view? 是否最好从代表车辆的视图或更大的视图中获取触摸事件? Is there a simple paradigm you've used for drag and drop that you're satisfied with? 有没有一个简单的范例,你用来拖放你满意吗? What are the drawbacks of different strategies? 不同策略的缺点是什么?

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): 假设您有一个带有背景图像和许多vehiclesUIView场景,您可以将每辆新车定义为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];

Then you may move the vehicle wherever you want, by responding to the UIControlEventTouchDragInside event, eg: 然后,您可以通过响应UIControlEventTouchDragInside事件来移动车辆,例如:

- (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. 与管理整个场景相比,单个车辆处理自己的拖车要容易得多。

In addition to ohho's answer I've tried similar implementation, but without problem of "fast" drag and centering to drag. 除了ohho的答案,我尝试过类似的实现,但没有“快速”拖动和居中拖动的问题。

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. 不过我发现这是最简单的拖动解决方案。

I had a case where I would like to drag/drop uiviews between multiple other uiviews. 我有一个案例,我想在多个其他uiviews之间拖放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. 不将事件侦听器添加到实际拖动视图的主要原因是,一旦我更改了拖动视图的superview,我就丢失了正在进行的pan事件。

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 我创建了一个可以在这里看到的简单示例: 在多个其他uiviews之间拖放uiviews

If you decide to go the other way, try using uicontrol instead of uibutton. 如果您决定采用其他方式,请尝试使用uicontrol而不是uibutton。 They declare the addTarget methods and are much easier to extend - hence give custom state and behavior. 它们声明了addTarget方法并且更容易扩展 - 因此提供自定义状态和行为。

-(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. ohho的回答对我很有用。 In case you need the swift 2.x 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
}

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. 如果你向UIView添加一些“可拖动的”视图并处理它们如何被拖动,我发现它是更干净的代码。 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. 我使用了对父UIView的简单回调来检查新位置是否合适 - 所以我可以用动画来指示。

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. 顶视图轨道拖动我猜是好的,但如果你想添加仍然与用户交互的不可拖动视图(如按钮),这会使它稍微混乱。

For Swift 4 Easy and easiest way. 对于Swift 4简单易行的方式。 😛 😛

Step 1: Connect Your Button From Storyboard To View Controller. 第1步:将您的按钮从故事板连接到视图控制器。 And Set The all Basic AutoLayout Constraints 并设置所有基本AutoLayout约束

 @IBOutlet weak var cameraButton: UIButton!

Step 2: Add Pan Gesture for your button In viewDidLoad() 第2步:为您的按钮添加Pan Gesture在viewDidLoad()中

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

Step 3: 第3步:

Add panGestureHandler 添加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")
    }

在此输入图像描述

Add See the Result ☝️ 添加查看结果☝️

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM