在拖放过程中在NSTableView中打开间隙 [英] Opening a gap in NSTableView during drag and drop

查看:66
本文介绍了在拖放过程中在NSTableView中打开间隙的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的单列基于视图的NSTableView,其中包含可以拖动以重新排序的项目.在拖放过程中,我想这样做,以便在鼠标下方的位置上打开要删除的项目的间隙.当您拖动以重新排列曲目时,GarageBand会执行类似的操作(此处的视频: http://www.screencast.com/t/OmUVHcCNS1 ).据我所知,NSTableView中没有对此的内置支持.

I've got a simple, single-column, view-based NSTableView with items in it that can be dragged to reorder them. During drag and drop, I'd like to make it so that a gap for the item-to-be-dropped opens up at the location under the mouse. GarageBand does something like this when you drag to reorder tracks (video here: http://www.screencast.com/t/OmUVHcCNSl). As far as I can tell, there's no built in support for this in NSTableView.

是否还有其他人试图将这种行为添加到NSTableView中并找到了一个好的解决方案?我已经考虑过并尝试了几种方法,但没有取得太大的成功.我的第一个想法是,在拖动过程中,通过在数据源的-tableView:validateDrop:...方法中发送-noteHeightOfRowsWithIndexesChanged:,然后在-tableView:heightOfRow:中返回正常高度的两倍,将鼠标下方的行高加倍.不幸的是,据我所知,NSTableView不会在拖放过程中更新其布局,因此尽管调用了noteHeightOfRowsWithIndexesChanged:,但行高度实际上并未更新.

Has anyone else tried to add this behavior to NSTableView and found a good solution? I've thought of and tried a couple approaches without much success. My first thought was to double the height of the row under the mouse during a drag by sending -noteHeightOfRowsWithIndexesChanged: in my data source's -tableView:validateDrop:... method, then returning twice the normal height in -tableView:heightOfRow:. Unfortunately, best I can tell, NSTableView doesn't update its layout during drag and drop, so despite calling noteHeightOfRowsWithIndexesChanged:, the row height isn't actually updated.

请注意,我正在使用基于视图的NSTableView,但是我的行并不那么复杂,以至于如果这样做有助于实现这一点,就无法移至基于单元格的表视图.我知道在拖动完成后可以轻松,内置地为放置的物品设置间隙的动画.我正在寻找一种在拖动进行过程中打开间隙的方法.另外,这是为了在Mac App Store中出售一个应用程序,因此它不得使用私有API.

Note that I'm using a view-based NSTableView, but my rows are not so complex that I couldn't move to a cell-based table view if doing so helped accomplish this. I'm aware of the easy, built-in ability to animate a gap for the dropped item after a drag is complete. I'm looking for a way to open a gap while the drag is in progress. Also, this is for an app to be sold in the Mac App Store, so it must not use private API.

我刚刚向Apple提出了增强请求,请求对此行为提供内置支持: http://openradar.appspot.com/12662624 . 如果您也想看到它,也请复制.更新:我请求的增强功能是在OS X 10.9 Mavericks中实现的,现在可以使用NSTableView API来实现此行为.参见

I've just filed an enhancement request with Apple requesting built in support for this behavior: http://openradar.appspot.com/12662624. Dupe if you'd like to see it too. Update: The enhancement I requested was implemented in OS X 10.9 Mavericks, and this behavior is now available using NSTableView API. See NSTableViewDraggingDestinationFeedbackStyleGap.

推荐答案

注意:现在,可以使用OS X 10.9 Mavericks和更高版本上的NSTableView中的内置API使用此问题和答案描述的行为.参见

Note: The behavior this question and answer describes are now available using built in API in NSTableView on OS X 10.9 Mavericks and later. See NSTableViewDraggingDestinationFeedbackStyleGap.

如果在针对OS X 10.8或更早版本的应用中需要此行为,则此答案可能仍然有用. 原始答案如下:

This answer may still be useful if this behavior is needed in an app targeting OS X 10.8 or earlier. Original answer below:

我现在已经实现了.我的基本方法如下:

I've implemented this now. My basic approach looks like this:

@interface ORSGapOpeningTableView : NSTableView

@property (nonatomic) NSInteger dropTargetRow;
@property (nonatomic) CGFloat heightOfDraggedRows;

@end

@implementation ORSGapOpeningTableView

#pragma mark - Dragging

- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
{
    NSInteger oldDropTargetRow = self.dropTargetRow;
    NSDragOperation result = [super draggingUpdated:sender];
    CGFloat imageHeight = [[sender draggedImage] size].height;
    self.heightOfDraggedRows = imageHeight;

    NSMutableIndexSet *changedRows = [NSMutableIndexSet indexSet];
    if (oldDropTargetRow > 0) [changedRows addIndex:oldDropTargetRow-1];
    if (self.dropTargetRow > 0) [changedRows addIndex:self.dropTargetRow-1];
    [self noteHeightOfRowsWithIndexesChanged:changedRows];

    return result;
}

- (void)draggingExited:(id<NSDraggingInfo>)sender
{
    self.dropTargetRow = -1;
    [self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];

    [super draggingExited:sender];
}

- (void)draggingEnded:(id<NSDraggingInfo>)sender
{
    self.dropTargetRow = -1;
    self.heightOfDraggedRows = 0.0;
    self.draggedRows = nil;
    [self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];
}

- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
{
    self.dropTargetRow = -1;
    self.heightOfDraggedRows = 0.0;
    self.draggedRows = nil;
    [self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];

    return [super performDragOperation:sender];
}

//在我的代表和数据源中:

// In my delegate and data source:

- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation
{
    if (dropOperation == NSTableViewDropOn) 
    {
        dropOperation = NSTableViewDropAbove;
        [self.tableView setDropRow:++row dropOperation:dropOperation];
    }

    NSDragOperation result = [self.realDataSource tableView:tableView validateDrop:info proposedRow:row proposedDropOperation:dropOperation];
    if (result != NSDragOperationNone) 
    {
        self.tableView.dropTargetRow = row;
    } 
    else 
    {
        self.tableView.dropTargetRow = -1; // Don't open a gap
    }
    return result;
}

- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
    CGFloat result = [tableView rowHeight];

    if (row == self.tableView.dropTargetRow - 1 && row > -1)
    {
        result += self.tableView.heightOfDraggedRows;
    }

    return result;
}

请注意,这是简化的代码,而不是我程序中的逐字复制/粘贴.我实际上最终将所有这些都包含在使用代理委托和数据源对象的NSTableView子类中,因此上述数据源/委托方法中的代码实际上位于代理对真正委托和数据源的调用的拦截中.这样,实际的数据源和委托不必执行任何特殊操作即可获得缺口打开行为.此外,有时表视图动画有些松懈,这不适用于第一行上方的拖动(由于没有行可以拉高,因此没有间隙被打开).总而言之,尽管还有进一步改进的余地,但是这种方法仍然相当有效.

Note that this is simplified code, not a verbatim copy/paste from my program. I actually ended up making this all contained within an NSTableView subclass that uses proxy delegate and data source objects so the code in data source/delegate methods above is actually inside the proxies' intercept of the calls to the real delegate and data source. That way, the real data source and delegate don't have to do anything special to get the gap opening behavior. Also, there's sometimes a little flakiness with the table view animations, and this doesn't work for drags above the first row (no gap is opened since there's no row to make taller). All in all, despite the room for further improvement, this approach works reasonably well.

我仍然想尝试类似的方法,但是插入一个空白行(如Caleb建议的那样)而不是更改行高.

I'd still like to try a similar approach, but insert a blank row (as Caleb suggested) instead of changing the row height.

这篇关于在拖放过程中在NSTableView中打开间隙的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆