How to create a drag and drop similar to setting up the Safari toolbar for iPad

I have a UIViewController on which there are UITable and UIView. I want to be able to collect the elements displayed in a UIView and transfer them to a cell in a UITableView. I had to revert to using touch events rather than the new UIGestureRecognisers to take a snapshot of the UIView so that this click drags onto the UITableView, not the UIView. This works fine using the following:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *t =[touches anyObject]; UIGraphicsBeginImageContext(t.view.bounds.size); [t.view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.draggedView = [[UIImageView alloc] initWithImage:img]; CGPoint centre = [[touches anyObject] locationInView:self.view]; [self.draggedView setCenter:centre]; [self.draggedView setAlpha:0.5]; [self.view addSubview:self.draggedView]; } 

However, in the touchesEnded event, when I try to evaluate which UIView finished touching, I always get a UIView instead of a UITableView when I throw it. Any ideas would be very welcome.

+2
source share
2 answers

I think I came up with a better solution to this problem using the latest GestureRecognisers. I use the following LongPress gesture recognizer inside my base TableView controller.

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]){ [gestureRecognizer addTarget:self action:@selector(longGestureAction:)]; } return YES; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES; } -(void)longGestureAction:(UILongPressGestureRecognizer *)gesture{ UITableViewCell *cell= (UITableViewCell *)[gesture view]; switch ([gesture state]) { case UIGestureRecognizerStateBegan:{ NSIndexPath *ip = [self.tableView indexPathForCell:cell]; [self.tableView setScrollEnabled:NO]; if(ip!=nil){ [self.draggableDelegate dragAndDropTableViewController:self draggingGestureWillBegin:gesture forCell:cell]; UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self ]; //switch the view the gesture is associated with this will allow the dragged view to continue on where the cell leaves off from [draggedView addGestureRecognizer:[[cell gestureRecognizers]objectAtIndex:0]]; [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidBegin:gesture forCell:cell]; } } break; case UIGestureRecognizerStateChanged:{ [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidMove:gesture]; } break; case UIGestureRecognizerStateEnded:{ UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self]; if(draggedView==nil) return; //this does not seem like the best way to do this yet you really don't want to fire one after the other I don't think [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidEnd:gesture]; [self.dropableDelegate dragAndDropTableViewController:self droppedGesture:gesture]; [self.tableView setScrollEnabled:YES]; [self.tableView reloadData]; } break; // case UIGestureRecognizerStateCancelled: // case UIGestureRecognizerStateFailed: // case UIGestureRecognizerStatePossible: // [self.dragAndDropDelegate dragAndDropTableViewController:self draggingGesture:gesture endedForItem:nil]; break; default: break; } } 

In the TableViews that extend this base class, I add the following to each cell in cellForIndexPath TableViewDataSource.

 UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:cell action:nil]; longPress.delegate = self; [cell addGestureRecognizer:longPress]; 

and finally, all you have to do is implement delegate methods, like

 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{ if([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]){ [gestureRecognizer addTarget:self action:@selector(longGestureAction:)]; } return YES; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES; } -(void)longGestureAction:(UILongPressGestureRecognizer *)gesture{ UITableViewCell *cell= (UITableViewCell *)[gesture view]; switch ([gesture state]) { case UIGestureRecognizerStateBegan:{ NSIndexPath *ip = [self.tableView indexPathForCell:cell]; [self.tableView setScrollEnabled:NO]; if(ip!=nil){ [self.draggableDelegate dragAndDropTableViewController:self draggingGestureWillBegin:gesture forCell:cell]; UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self ]; //switch the view the gesture is associated with this will allow the dragged view to continue on where the cell leaves off from [draggedView addGestureRecognizer:[[cell gestureRecognizers]objectAtIndex:0]]; [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidBegin:gesture forCell:cell]; } } break; case UIGestureRecognizerStateChanged:{ [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidMove:gesture]; } break; case UIGestureRecognizerStateEnded:{ UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self]; if(draggedView==nil) return; //this does not seem like the best way to do this yet you really don't want to fire one after the other I don't think [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidEnd:gesture]; [self.dropableDelegate dragAndDropTableViewController:self droppedGesture:gesture]; [self.tableView setScrollEnabled:YES]; [self.tableView reloadData]; } break; // case UIGestureRecognizerStateCancelled: // case UIGestureRecognizerStateFailed: // case UIGestureRecognizerStatePossible: // [self.dragAndDropDelegate dragAndDropTableViewController:self draggingGesture:gesture endedForItem:nil]; break; default: break; } } 

Here you can find the source here . It took me a long time to get this right, so I really hope this saves someone else.

+2
source

So, the solution I came up with allows me to drag the view and drop it onto the table view cell, setting the cell image property according to the dragged UIView as follows.

At first I had to implement touchMoved to move the alpha mixed view when it is dragged to the cell it falls on.

 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ CGPoint centre = [[touches anyObject] locationInView:self.view]; [self.draggedView setCenter:centre]; } 

Then on touchs.Ended, I did a post test that returns a UITableViewCellContentView (UIView * hit). Asking for a look at viewing views, you get a hit on the cell, allowing you to set its image.

 -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ CGPoint centre = [[touches anyObject] locationInView:self.view]; UIView *hit = [self.tableView hitTest:centre withEvent:event]; UIGraphicsBeginImageContext(self.draggedView.bounds.size); [self.draggedView.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); if(hit!=nil){ UITableViewCell *tvc = (UITableViewCell *)hit.superview; [tvc.imageView setImage:img]; } [self.draggedView removeFromSuperview]; self.draggedView = nil; } 

Hope this helps someone.

+1
source

Source: https://habr.com/ru/post/1381535/


All Articles