I'm working on a D&D in a UISplitViewController (based on Xcode template project), from the MasterViewController to the DetailViewController.
Basically, what I'm doing is creating a UILongPressGestureRecognizer and placing it on the self.tableview property of the MasterViewController.
Below is my gesture recognizer.
- (void)gestureHandler:(UIGestureRecognizer*)gesture
{
CGPoint location;
NSIndexPath* indexPath = [self.tableView indexPathForRowAtPoint:[gesture locationInView:self.tableView]];
if (gesture.state == UIGestureRecognizerStateBegan) {
// Create draggable view
NSString* imageCanvasName = [self imageNameByIndexPath:indexPath isDetail:YES];
UIImage* imageCanvas = [UIImage imageNamed:imageCanvasName];
_draggedView = [[UIImageView alloc] initWithImage:imageCanvas];
// Create drag-feedback window, add the drag-view and make the drag-window visible
CGRect windowFrame = self.view.window.frame;
_dragFeedbackWindow = [[UIWindow alloc] initWithFrame:windowFrame];
location = [gesture locationInView:gesture.view.window];
[_draggedView setCenter:location];
[_dragFeedbackWindow addSubview:_draggedView];
[_dragFeedbackWindow setHidden:NO];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
// Update drag-view location
location = [gesture locationInView:gesture.view.window];
[_draggedView setCenter:location];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// Disconnect drag-view and hide drag-feedback window
[_draggedView removeFromSuperview];
[_dragFeedbackWindow setHidden:YES];
// If drop is in a valid location...
if ([self.tableView pointInside:_draggedView.center withEvent:nil] == NO)
{
// Get final location in detailViewController coordinates
location = [gesture locationInView:self.detailViewController.view];
[_draggedView setCenter:location];
[self.detailViewController.view addSubview:_draggedView];
}
}
else {
NSLog(@"%s unrecognized gesture %d", __FUNCTION__, gesture.state);
}
}
All works very nicely when the iPad is in portrait mode - the drag, the drop - the works.
My problem starts if the iPad is rotated... In such a case, my _draggedView appears "counter-rotated" - it will "reflect" the iPad's rotation - until dropped.
It's like I must apply some rotation to _dragFeedbackWindow - but I tried a number of things, failing...
Any idea?
Thanks!
Update your iPhone to iOS 15 to use the drag and drop feature. Long-press on a text, URL, image, or document to select it from the source app. Drag and drop the selected content on the appropriate location on the destination app.
OK - I figured out the "WHY" this happens, and the "HOW" to fix (and will attach the handler code to do this correctly, all below.
Here's the code... "Just" add it to your MAsterViewController (if - like me - you want to drag from the master to the detail...)
// A simple UIView extension to rotate it to a given orientation
@interface UIView(oriented)
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation;
@end
@implementation UIView(oriented)
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation {
CGFloat angle = 0.0;
switch (orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = - M_PI / 2.0f;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI / 2.0f;
break;
default: // as UIInterfaceOrientationPortrait
angle = 0.0;
break;
}
self.transform = CGAffineTransformMakeRotation(angle);
}
Now, the LongPress gesture handler...
- (void)gestureHandler:(UIGestureRecognizer*)gesture
{
CGPoint location;
UIWindow* dragFeedback = [UIApplication sharedApplication].delegate.window;
if (gesture.state == UIGestureRecognizerStateBegan) {
// Create draggable view
_draggedView = [[UIImageView alloc] initWithImage:@"someImage.png"];
// Required to adapt orientation... WORKS, BUT WHY NEEDED???
[_draggedView rotateToOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
// Create drag-feedback window, add the drag-view and make the drag-window visible
location = [gesture locationInView:dragFeedback];
[_draggedView setCenter:location];
[dragFeedback addSubview:_draggedView];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
// Update drag-view location
location = [gesture locationInView:dragFeedback];
[_draggedView setCenter:location];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// Disconnect drag-view and hide drag-feedback window
[_draggedView removeFromSuperview];
// Get final location in detailViewController coordinates
location = [gesture locationInView:self.detailViewController.view];
[_draggedView setCenter:location];
// "Noramlize" orientation... WORKS, BUT WHY NEEDED???
[_draggedView rotateToOrientation:UIInterfaceOrientationPortrait];
[self.detailViewController.view addSubview:_draggedView];
}
else {
NSLog(@"%s unrecognized gesture %d", __FUNCTION__, gesture.state);
}
}
This works like a charm - let me know how it works for you (if you need a sample project, let me know...)
Thanks for your solution! You got me 90% of the way there. In payment, I will give you the last 10% :)
The issue seems to be that the UIWindow never gets rotated, only its subviews do! So the solution is to use a subview.
I've also added some code that shows how to see which cell is selected (assuming your master view is a UITableViewController).
- (IBAction)handleGesture:(UIGestureRecognizer *)gesture
{
CGPoint location;
UIWindow *window = [UIApplication sharedApplication].delegate.window;
//The window doesn't seem to rotate, so we'll get the subview, which does!
UIView *dragFeedback = [window.subviews objectAtIndex:0];
if (gesture.state == UIGestureRecognizerStateBegan) {
location = [gesture locationInView:dragFeedback];
//which cell are we performing the long hold over?
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:[gesture locationInView:self.tableView]];
UITableViewCell *selecteCell = [self.tableView cellForRowAtIndexPath:indexPath];
NSLog(@"Cell %@", selecteCell);
// Create draggable view
_draggedView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"someImage.png"]];
// Create drag-feedback window, add the drag-view and make the drag-window visible
[_draggedView setCenter:location];
[dragFeedback addSubview:_draggedView];
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
// Update drag-view location
location = [gesture locationInView:dragFeedback];
[_draggedView setCenter:location];
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
// Disconnect drag-view and hide drag-feedback window
[_draggedView removeFromSuperview];
// Get final location in detailViewController coordinates
location = [gesture locationInView:self.detailViewController.view];
[_draggedView setCenter:location];
[self.detailViewController.view addSubview:_draggedView];
}
else {
NSLog(@"%s unrecognized gesture %d", __FUNCTION__, gesture.state);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With