Reputation: 3128
I have a UIViewController that has both a UITable and a UIView on it. I want to be able to pick up items that are displayed in the UIView and drop them on to a cell in the UITableView.I have had to revert to using touch events as opposed to the new UIGestureRecognisers to take a snap shot of the UIView tapped so that this snap shot is dragged over to the UITableView as opposed to the UIView touched. This works great 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 the touch ended on I always get a UIView instead of the UITableView when I drop on it. Any ideas would be very welcome.
Upvotes: 2
Views: 1836
Reputation: 3128
I think I have come up with better solution to this problem using the latest GestureRecognisers. I use the following LongPress Gesture Recogniser 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 the cellForIndexPath TableViewDataSource,
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:cell action:nil];
longPress.delegate = self;
[cell addGestureRecognizer:longPress];
and finally all you need to do is implement the delegate methods like so,
- (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;
}
}
You can find the source for this here. It took me a long time to get this right so I really hope this saves someone else the time.
Upvotes: 2
Reputation: 3128
So the solution that I have come up with that allows me to drag a view and drop it onto a table view cell, setting the cells image property to match the UIView being dragged is as follows.
First I had to implement the touchMoved to move the alpha blended view as it is dragged across to the cell it is being dropped on.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint centre = [[touches anyObject] locationInView:self.view];
[self.draggedView setCenter:centre];
}
Then on the touchesEnded I did a hit test on the tableview which returns a UITableViewCellContentView (UIView *hit). By asking for hit view's superview you get the cell hit 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.
Upvotes: 1