23

ドラッグして並べ替えることができるアイテムを含む、シンプルな単一列のビューベースの NSTableView があります。ドラッグアンドドロップの際、マウスの下の位置にドロップするアイテムの隙間が開くようにしたいです。ドラッグしてトラックを並べ替えると、GarageBand はこのような処理を行います (ビデオはこちら: http://www.screencast.com/t/OmUVHcCNSl )。私が知る限り、NSTableView にはこれに対するサポートが組み込まれていません。

他の誰かがこの動作を NSTableView に追加しようとして、良い解決策を見つけましたか? 私はいくつかのアプローチを考えて試しましたが、あまり成功しませんでした。私が最初に考えたのは、ドラッグ中にマウスの下にある行の高さを 2 倍にすることでした。-noteHeightOfRowsWithIndexesChanged:データ ソースの-tableView:validateDrop:...メソッドを送信してから、通常の高さの 2 倍を に返し-tableView:heightOfRow:ます。残念ながら、NSTableView はドラッグ アンド ドロップ中にレイアウトを更新しないため、 を呼び出してもnoteHeightOfRowsWithIndexesChanged:行の高さは実際には更新されません。

私はビューベースの NSTableView を使用していますが、私の行はそれほど複雑ではないので、セルベースのテーブル ビューに移動することはできませんでした。ドラッグが完了した後、ドロップされたアイテムのギャップをアニメーション化する簡単な組み込み機能を認識しています。ドラッグの進行中に隙間を開ける方法を探しています。また、これは Mac App Store で販売するアプリのため、プライベート API を使用してはいけません。

編集: この動作の組み込みサポートを要求する拡張要求を Apple に提出しました: http://openradar.appspot.com/12662624あなたもそれを見たいなら、だまされてください。更新: 私が要求した拡張機能は OS X 10.9 Mavericks で実装されました。この動作は NSTableView API を使用して利用できるようになりました。NSTableViewDraggingDestinationFeedbackStyleGapを参照してください。

4

4 に答える 4

9

これを行うのは奇妙に感じますが、作成者によって削除されたように見える非常に完全な回答がここのキューにあります。その中で、彼らは実用的なソリューションへの正しいリンクを提供しました。他の誰かがそうしたい場合はそれらを含めて、実行して実行するための答えとして提示する必要があると感じています。

のドキュメントから、NSTableView行のアニメーション効果に関する次の注意事項が隠されています。

行のアニメーション効果

テーブルビューが行または列の削除にフェードを使用することを指定するオプションの定数。NSTableViewAnimationOptionsこの効果は任意の定数と組み合わせることができます。

enum {
    NSTableViewAnimationEffectFade = 0x1,
    NSTableViewAnimationEffectGap = 0x2,
};

定数:

...

NSTableViewAnimationEffectGap

新しく挿入された行のギャップを作成します。これは、新しく開いたギャップにアニメーション化するドラッグ アンド ドロップ アニメーションに役立ち、デリゲート メソッドで使用する必要がありますtableView:acceptDrop:row:dropOperation:

Appleのサンプルコードを見ると、次のことがわかります。

- (void)_performInsertWithDragInfo:(id <NSDraggingInfo>)info parentNode:(NSTreeNode *)parentNode childIndex:(NSInteger)childIndex {
    // NSOutlineView's root is nil
    id outlineParentItem = parentNode == _rootTreeNode ? nil : parentNode;
    NSMutableArray *childNodeArray = [parentNode mutableChildNodes];
    NSInteger outlineColumnIndex = [[_outlineView tableColumns] indexOfObject:[_outlineView outlineTableColumn]];

    // Enumerate all items dropped on us and create new model objects for them    
    NSArray *classes = [NSArray arrayWithObject:[SimpleNodeData class]];
    __block NSInteger insertionIndex = childIndex;
    [info enumerateDraggingItemsWithOptions:0 forView:_outlineView classes:classes searchOptions:nil usingBlock:^(NSDraggingItem *draggingItem, NSInteger index, BOOL *stop) {
        SimpleNodeData *newNodeData = (SimpleNodeData *)draggingItem.item;
        // Wrap the model object in a tree node
        NSTreeNode *treeNode = [NSTreeNode treeNodeWithRepresentedObject:newNodeData];
        // Add it to the model
        [childNodeArray insertObject:treeNode atIndex:insertionIndex];
        [_outlineView insertItemsAtIndexes:[NSIndexSet indexSetWithIndex:insertionIndex] inParent:outlineParentItem withAnimation:NSTableViewAnimationEffectGap];
        // Update the final frame of the dragging item
        NSInteger row = [_outlineView rowForItem:treeNode];
        draggingItem.draggingFrame = [_outlineView frameOfCellAtColumn:outlineColumnIndex row:row];

        // Insert all children one after another
        insertionIndex++;
    }];

}

本当にこれほど単純なのかどうかはわかりませんが、少なくとも検討する価値はありますが、ニーズを満たしていない場合は完全に拒否されます.

編集:正しい解決策に従う手順については、この回答のコメントを参照してください。OP はより完全な回答を投稿しました。これは、同じ問題の解決策を探している人が参照する必要があります。

于 2012-04-17T00:40:35.593 に答える
8

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.

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;
}

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.

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

于 2012-04-28T21:12:04.513 に答える
6

Mac OS X 10.9 (Mavericks) の時点で、NSTableView でドラッグ アンド ドロップをアニメーション化するためのはるかに簡単なソリューションがあります。

[aTableView setDraggingDestinationFeedbackStyle:NSTableViewDraggingDestinationFeedbackStyleGap];

テーブル ビューは、行がドラッグされると、アニメーション付きのギャップを自動的に挿入します。これは、古い青い線の挿入ポイントの方法よりもはるかに優れています。

于 2014-06-27T18:07:35.620 に答える
3

あなたが求めていることを達成する 1 つの方法は、提案されたドロップ ポイント (つまり、最も近い 2 つの行の間) に空の行を挿入することです。の使用を検討しているように聞こえますがNSTableViewAnimationEffectGap、これは、ドロップが で受け入れられたときに挿入をアニメーション化することを実際に意図していることに注意してください-tableView:acceptDrop:row:dropOperation:

ユーザーがマウス ボタンを離して実際にドロップするにギャップを開きたいので、代わりに-insertRowsAtIndexes:withAnimation:テーブルの-draggingUpdate:メソッドから を使用して空白行を挿入し、同時に を使用してこのドラッグ用に以前に挿入した空白行を削除することができます-removeRowsAtIndexes:withAnimation:。必要に応じて、これらの操作のアニメーションとしてNSTableViewAnimationSlideUpとを使用します。NSTableViewAnimationSlideDown

于 2012-04-22T18:00:45.643 に答える