【问题标题】:ipad: Drag a UITableViewCell from one UITableViewController and drop it into anotheripad:将 UITableViewCell 从一个 UITableViewController 拖放到另一个
【发布时间】:2011-09-24 07:37:12
【问题描述】:

我的问题是,这是否可能,如果可以,您将如何处理这项任务?

是否有人知道已经使用此功能的应用程序,或者从哪里获得一些示例代码?

另外,如果我要在不了解该主题的情况下自行实现此功能,您估计完成它需要多长时间?

还有一件事要提一下,因为它可能会使事情变得更复杂:目标表默认处于编辑模式,以便用户可以重新排列已删除的单元格(使用附件视图中的标准重新排序控件)在那里。

编辑:

我只是尝试在我的帖子中包含概念图像的屏幕截图。图像在左侧显示了一个表格,在右侧显示了一个灰色的放置区域。我的客户说他也在其他应用上看到了这个,所以肯定有某种我不知道的 ui 元素。

我在开发者库中没有找到任何关于这样一个放置区域的信息,所以希望你们中的某个人能给我指路或清理一下。

【问题讨论】:

标签: ios ipad drag-and-drop


【解决方案1】:

好的,我自己实现了这个,我对结果很满意。您可以将单元格从左侧(源表)拖动到右侧(目标表),也可以在目标表中拖动单元格进行重新排序。如果您尝试从右向左拖动一个单元格,它将重新插入到您开始拖动的相同位置(因此没有任何反应)。目标表还支持删除单元格,源表不支持。完整代码如下:

UIDropTableViewController.h

#import <UIKit/UIKit.h>

@interface UIDropTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate>
{
    UINavigationItem*   srcTableNavItem;
    UINavigationItem*   dstTableNavItem;

    UITableView*        srcTableView;
    UITableView*        dstTableView;
    UITableViewCell*    draggedCell;
    UIView*             dropArea;

    NSMutableArray*     srcData;
    NSMutableArray*     dstData;
    id                  draggedData;

    BOOL            dragFromSource;     // used for reodering
    NSIndexPath*    pathFromDstTable;   // used to reinsert data when reodering fails
}

@property (nonatomic, readonly) NSArray* srcData;
@property (nonatomic, readonly) NSArray* dstData;

- (id)initWithFrame:(CGRect)frame SourceData:(NSArray*)sourceData DestinationData:(NSArray*)destinationData;

- (void)setSrcTableTitle:(NSString*)title;
- (void)setDstTableTitle:(NSString*)title;

@end

UIDropTableViewController.m

#import "UIDropTableViewController.h"

#define kCellIdentifier @"DropTableCell"
#define kCellHeight 44
#define kNavBarHeight 30


// forward declaration of private helper methods
@interface UIDropTableViewController()

- (void)setupSourceTableWithFrame:(CGRect)frame;
- (void)setupDestinationTableWithFrame:(CGRect)frame;
- (void)initDraggedCellWithCell:(UITableViewCell*)cell AtPoint:(CGPoint)point;

- (void)startDragging:(UIPanGestureRecognizer *)gestureRecognizer;
- (void)startDraggingFromSrcAtPoint:(CGPoint)point;
- (void)startDraggingFromDstAtPoint:(CGPoint)point;

- (void)doDrag:(UIPanGestureRecognizer *)gestureRecognizer;
- (void)stopDragging:(UIPanGestureRecognizer *)gestureRecognizer;

- (UITableViewCell*)srcTableCellForRowAtIndexPath:(NSIndexPath*)indexPath;
- (UITableViewCell*)dstTableCellForRowAtIndexPath:(NSIndexPath*)indexPath;

@end


@implementation UIDropTableViewController

@synthesize srcData, dstData;

#pragma mark -
#pragma mark Public Methods

- (void)setSrcTableTitle:(NSString*)title
{
    srcTableNavItem.title = title;
}

- (void)setDstTableTitle:(NSString*)title
{
    dstTableNavItem.title = title;
}

#pragma mark -
#pragma mark UIViewController

- (id)initWithFrame:(CGRect)frame SourceData:(NSArray*)sourceData DestinationData:(NSArray*)destinationData
{
    self = [super init];
    if (self)
    {
        self.view.clipsToBounds = YES;
        self.view.frame = frame;
        int width = frame.size.width;
        int height = frame.size.height;

        // set up data
        srcData = [[NSMutableArray alloc] initWithArray:sourceData];
        dstData = [[NSMutableArray alloc] initWithArray:destinationData];

        draggedCell = nil;
        draggedData = nil;
        pathFromDstTable = nil;

        // set up views
        [self setupSourceTableWithFrame:CGRectMake(0, 0, width / 2, height)];
        [self setupDestinationTableWithFrame:CGRectMake(width / 2, 0, width / 2, height)];

        UIView* separator = [[UIView alloc] initWithFrame:CGRectMake(width / 2, 0, 1, height)];
        separator.backgroundColor = [UIColor blackColor];
        [self.view addSubview:separator];
        [separator release];

        // set up gestures
        UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanning:)];
        [self.view addGestureRecognizer:panGesture];
        [panGesture release];
    }
    return self;
}

- (void)dealloc
{
    [srcTableNavItem release];
    [dstTableNavItem release];

    [srcTableView release];
    [dstTableView release];
    [dropArea release];

    [srcData release];
    [dstData release];

    if(draggedCell != nil)
        [draggedCell release];
    if(draggedData != nil)
        [draggedData release];
    if(pathFromDstTable != nil)
        [pathFromDstTable release];

    [super dealloc];
}

- (void)viewWillAppear:(BOOL)animated
{
    [srcTableView reloadData];
    [dstTableView reloadData];

    [UIView animateWithDuration:0.2 animations:^
     {
         CGRect frame = dstTableView.frame;
         frame.size.height = kCellHeight * [dstData count];
         dstTableView.frame = frame;
     }];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

#pragma mark -
#pragma mark Helper methods for initialization

- (void)setupSourceTableWithFrame:(CGRect)frame
{
    srcTableNavItem = [[UINavigationItem alloc] init];
    srcTableNavItem.title = @"Source Table";

    CGRect navBarFrame = frame;
    navBarFrame.size.height = kNavBarHeight;

    UINavigationBar* navigationBar = [[UINavigationBar alloc] initWithFrame:navBarFrame];
    [navigationBar pushNavigationItem:srcTableNavItem animated:false];
    [navigationBar setTintColor:[UIColor lightGrayColor]];
    [self.view addSubview:navigationBar];
    [navigationBar release];

    CGRect tableFrame = frame;
    tableFrame.origin.y = kNavBarHeight;
    tableFrame.size.height -= kNavBarHeight;

    srcTableView = [[UITableView alloc] initWithFrame:tableFrame];
    [srcTableView setDelegate:self];
    [srcTableView setDataSource:self];
    [self.view addSubview:srcTableView];
}

- (void)setupDestinationTableWithFrame:(CGRect)frame
{
    dstTableNavItem = [[UINavigationItem alloc] init];
    dstTableNavItem.title = @"Destination Table";

    CGRect navBarFrame = frame;
    navBarFrame.size.height = kNavBarHeight;

    UINavigationBar* navigationBar = [[UINavigationBar alloc] initWithFrame:navBarFrame];
    [navigationBar pushNavigationItem:dstTableNavItem animated:false];
    [navigationBar setTintColor:[UIColor lightGrayColor]];
    [self.view addSubview:navigationBar];
    [navigationBar release];

    CGRect dropAreaFrame = frame;
    dropAreaFrame.origin.y = kNavBarHeight;
    dropAreaFrame.size.height -= kNavBarHeight;

    dropArea = [[UIView alloc] initWithFrame:dropAreaFrame];
    [dropArea setBackgroundColor:[UIColor grayColor]];
    [self.view addSubview:dropArea];

    CGRect contentFrame = dropAreaFrame;
    contentFrame.origin = CGPointMake(0, 0);

    UILabel* dropAreaLabel = [[UILabel alloc] initWithFrame:contentFrame];
    dropAreaLabel.backgroundColor = [UIColor clearColor];
    dropAreaLabel.font = [UIFont boldSystemFontOfSize:12];
    dropAreaLabel.textAlignment = UITextAlignmentCenter;
    dropAreaLabel.textColor = [UIColor whiteColor];
    dropAreaLabel.text = @"Drop items here...";
    [dropArea addSubview:dropAreaLabel];
    [dropAreaLabel release];

    CGRect tableFrame = contentFrame;
    tableFrame.size.height = kCellHeight * [dstData count];

    dstTableView = [[UITableView alloc] initWithFrame:tableFrame];
    [dstTableView setEditing:YES];
    [dstTableView setDelegate:self];
    [dstTableView setDataSource:self];
    [dropArea addSubview:dstTableView];
}

- (void)initDraggedCellWithCell:(UITableViewCell*)cell AtPoint:(CGPoint)point
{
    // get rid of old cell, if it wasn't disposed already
    if(draggedCell != nil)
    {
        [draggedCell removeFromSuperview];
        [draggedCell release];
        draggedCell = nil;
    }

    CGRect frame = CGRectMake(point.x, point.y, cell.frame.size.width, cell.frame.size.height);

    draggedCell = [[UITableViewCell alloc] init];
    draggedCell.selectionStyle = UITableViewCellSelectionStyleGray;
    draggedCell.textLabel.text = cell.textLabel.text;
    draggedCell.textLabel.textColor = cell.textLabel.textColor;
    draggedCell.highlighted = YES;
    draggedCell.frame = frame;
    draggedCell.alpha = 0.8;

    [self.view addSubview:draggedCell];
}

#pragma mark -
#pragma mark UIGestureRecognizer

- (void)handlePanning:(UIPanGestureRecognizer *)gestureRecognizer
{
    switch ([gestureRecognizer state]) {
        case UIGestureRecognizerStateBegan:
            [self startDragging:gestureRecognizer];
            break;
        case UIGestureRecognizerStateChanged:
            [self doDrag:gestureRecognizer];
            break;
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateFailed:
            [self stopDragging:gestureRecognizer];
            break;
        default:
            break;
    }
}

#pragma mark -
#pragma mark Helper methods for dragging

- (void)startDragging:(UIPanGestureRecognizer *)gestureRecognizer
{
    CGPoint pointInSrc = [gestureRecognizer locationInView:srcTableView];
    CGPoint pointInDst = [gestureRecognizer locationInView:dstTableView];

    if([srcTableView pointInside:pointInSrc withEvent:nil])
    {
        [self startDraggingFromSrcAtPoint:pointInSrc];
        dragFromSource = YES;
    }
    else if([dstTableView pointInside:pointInDst withEvent:nil])
    {
        [self startDraggingFromDstAtPoint:pointInDst];
        dragFromSource = NO;
    }
}

- (void)startDraggingFromSrcAtPoint:(CGPoint)point
{
    NSIndexPath* indexPath = [srcTableView indexPathForRowAtPoint:point];
    UITableViewCell* cell = [srcTableView cellForRowAtIndexPath:indexPath];
    if(cell != nil)
    {
        CGPoint origin = cell.frame.origin;
        origin.x += srcTableView.frame.origin.x;
        origin.y += srcTableView.frame.origin.y;

        [self initDraggedCellWithCell:cell AtPoint:origin];
        cell.highlighted = NO;

        if(draggedData != nil)
        {
            [draggedData release];
            draggedData = nil;
        }
        draggedData = [[srcData objectAtIndex:indexPath.row] retain];
    }
}

- (void)startDraggingFromDstAtPoint:(CGPoint)point
{
    NSIndexPath* indexPath = [dstTableView indexPathForRowAtPoint:point];
    UITableViewCell* cell = [dstTableView cellForRowAtIndexPath:indexPath];
    if(cell != nil)
    {
        CGPoint origin = cell.frame.origin;
        origin.x += dropArea.frame.origin.x;
        origin.y += dropArea.frame.origin.y;

        [self initDraggedCellWithCell:cell AtPoint:origin];
        cell.highlighted = NO;

        if(draggedData != nil)
        {
            [draggedData release];
            draggedData = nil;
        }
        draggedData = [[dstData objectAtIndex:indexPath.row] retain];

        // remove old cell
        [dstData removeObjectAtIndex:indexPath.row];
        [dstTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
        pathFromDstTable = [indexPath retain];

        [UIView animateWithDuration:0.2 animations:^
         {
             CGRect frame = dstTableView.frame;
             frame.size.height = kCellHeight * [dstData count];
             dstTableView.frame = frame;
         }];

    }
}

- (void)doDrag:(UIPanGestureRecognizer *)gestureRecognizer
{
    if(draggedCell != nil && draggedData != nil)
    {
        CGPoint translation = [gestureRecognizer translationInView:[draggedCell superview]];
            [draggedCell setCenter:CGPointMake([draggedCell center].x + translation.x,
                                               [draggedCell center].y + translation.y)];
        [gestureRecognizer setTranslation:CGPointZero inView:[draggedCell superview]];
    }
}

- (void)stopDragging:(UIPanGestureRecognizer *)gestureRecognizer
{
    if(draggedCell != nil && draggedData != nil)
    {
        if([gestureRecognizer state] == UIGestureRecognizerStateEnded
           && [dropArea pointInside:[gestureRecognizer locationInView:dropArea] withEvent:nil])
        {            
            NSIndexPath* indexPath = [dstTableView indexPathForRowAtPoint:[gestureRecognizer locationInView:dstTableView]];
            if(indexPath != nil)
            {
                [dstData insertObject:draggedData atIndex:indexPath.row];
                [dstTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
            }
            else
            {
                [dstData addObject:draggedData];
                [dstTableView reloadData];
            }
        }
        else if(!dragFromSource && pathFromDstTable != nil)
        {
            // insert cell back where it came from
            [dstData insertObject:draggedData atIndex:pathFromDstTable.row];
            [dstTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:pathFromDstTable] withRowAnimation:UITableViewRowAnimationMiddle];

            [pathFromDstTable release];
            pathFromDstTable = nil;
        }

        [UIView animateWithDuration:0.3 animations:^
         {
             CGRect frame = dstTableView.frame;
             frame.size.height = kCellHeight * [dstData count];
             dstTableView.frame = frame;
         }];

        [draggedCell removeFromSuperview];
        [draggedCell release];
        draggedCell = nil;

        [draggedData release];
        draggedData = nil;
    }
}

#pragma mark -
#pragma mark UITableViewDataSource

- (BOOL)tableView:(UITableView*)tableView canMoveRowAtIndexPath:(NSIndexPath*)indexPath
{
    // disable build in reodering functionality
    return NO;
}

- (void)tableView:(UITableView*)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // enable cell deletion for destination table
    if([tableView isEqual:dstTableView] && editingStyle == UITableViewCellEditingStyleDelete)
    {
        [dstData removeObjectAtIndex:indexPath.row];
        [dstTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

        [UIView animateWithDuration:0.2 animations:^
         {
             CGRect frame = dstTableView.frame;
             frame.size.height = kCellHeight * [dstData count];
             dstTableView.frame = frame;
         }];
    }
}

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
    // tell our tables how many rows they will have
    int count = 0;
    if([tableView isEqual:srcTableView])
    {
        count = [srcData count];
    }
    else if([tableView isEqual:dstTableView])
    {
        count = [dstData count];
    }
    return count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return kCellHeight;
}

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    UITableViewCell* result = nil;
    if([tableView isEqual:srcTableView])
    {
        result = [self srcTableCellForRowAtIndexPath:indexPath];
    }
    else if([tableView isEqual:dstTableView])
    {
        result = [self dstTableCellForRowAtIndexPath:indexPath];
    }

    return result;
}

#pragma mark -
#pragma mark Helper methods for table stuff

- (UITableViewCell*)srcTableCellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    // tell our source table what kind of cell to use and its title for the given row
    UITableViewCell *cell = [srcTableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:kCellIdentifier] autorelease];

        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        cell.textLabel.textColor = [UIColor darkGrayColor];
    }
    cell.textLabel.text = [[srcData objectAtIndex:indexPath.row] description];

    return cell;
}

- (UITableViewCell*)dstTableCellForRowAtIndexPath:(NSIndexPath*)indexPath
{
    // tell our destination table what kind of cell to use and its title for the given row
    UITableViewCell *cell = [dstTableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:kCellIdentifier] autorelease];

        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        cell.textLabel.textColor = [UIColor darkGrayColor];
    }
    cell.textLabel.text = [[dstData objectAtIndex:indexPath.row] description];

    return cell;
}

@end

这是一个如何使用它的示例:

NSArray* srcData = [NSArray arrayWithObjects:@"item0", @"item1", @"item2", @"item3", @"item4", nil];
NSArray* dstData = [NSArray arrayWithObjects:@"item5", @"item6", nil];

dropTable = [[UIDropTableViewController alloc] initWithFrame:CGRectMake(100, 100, 600, 500) SourceData:srcData DestinationData:dstData];
[dropTable setSrcTableTitle:@"Bla"];
[dropTable setDstTableTitle:@"Blub"];
[[dropTable.view layer] setBorderColor:[[UIColor darkGrayColor] CGColor]];
[[dropTable.view layer] setBorderWidth:1];
[[dropTable.view layer] setCornerRadius:2];
[self.view addSubview:dropTable.view];

然后,在您完成编辑后,您只需阅读 dropTable.dstData 并继续使用它来做任何您想做的事情。

UIDropTableViewController.m 中,您可能需要根据自己的需要调整initDraggedCellWithCellsrcTableCellForRowAtIndexPathdstTableCellForRowAtIndexPath,就单元格表示而言。

【讨论】:

  • 我使用上面的代码作为我需要的基础。写得很有效率。谢谢
  • 很好的例子。我用 Swift 重写了它并修改了它以实现双向拖动,并且效果很好。
  • @rmooney 你想分享你的解决方案吗?
【解决方案2】:

我也只需要解决一个类似的问题。这是github上编译和运行示例的链接。

https://github.com/luminixinc/UITableViewDragAndDropExample.git

享受吧。

【讨论】:

    【解决方案3】:

    我知道这是一个非常古老的问题,但我最近在一个简单的视图助手类中实现了代码 christoph gave in his answer - 认为它可能有用:

    i3-dragndrop

    它支持UITableViewsUICollectionViews 作为源和目标。

    更新

    我刚刚实现了上述库的第 2 版:BetweenKit。它建立在原始概念的基础上,但支持广泛的新功能,例如多目标集合、自定义渲染等等。希望对您有所帮助!

    【讨论】:

      【解决方案4】:

      您可能已经弄清楚和/或忘记了这一点,但这是为任何想知道如何做到这一点并遇到此 stackoverflow 页面的人准备的。

      一个没有故事板和没有 ARC 的例子:https://github.com/josh2112/iPad-UITableView-DnD-Demo

      带有故事板和 ARC 的示例:https://github.com/erikthiem/TestDragAndDropStoryboardARC

      【讨论】:

        【解决方案5】:

        上面 christoph 的代码和 Tom K 的完整功能示例项目对我来说都是说明性的。

        Tom K 功能示例中的一个小点: https://github.com/luminixinc/UITableViewDragAndDropExample.git 从情节提要实例化的“UserDetailViewController”中的 IBOutlet 已损坏。

        解决方法 storyboard instantiateViewControllerWithIdentifier not setting IBOutlets

        【讨论】:

          猜你喜欢
          • 2013-11-23
          • 1970-01-01
          • 2016-11-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多