Jar*_*tar 16 macos drag-and-drop nstableview
我只是想知道是否有一种简单的方法来设置NSTableView以允许它重新排序其行而无需编写任何粘贴板代码.我只需要它就可以在一个表内部完成.我编写pboard代码没有问题,除了我很确定我看到Interface Builder在某个地方有一个切换/默认情况下看到它正常工作.这看起来似乎是一项非常普遍的任务.
谢谢
Eth*_*han 28
将表视图的数据源设置为符合的类NSTableViewDataSource.
将这个在适当的地方(-applicationWillFinishLaunching,-awakeFromNib,-viewDidLoad或类似的东西):
tableView.registerForDraggedTypes(["public.data"])
Run Code Online (Sandbox Code Playgroud)
然后实现这三种NSTableViewDataSource方法:
tableView:pasteboardWriterForRow:
tableView:validateDrop:proposedRow:proposedDropOperation:
tableView:acceptDrop:row:dropOperation:
Run Code Online (Sandbox Code Playgroud)
这是完全工作的代码,支持拖放重新排序多行:
func tableView(tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = NSPasteboardItem()
item.setString(String(row), forType: "public.data")
return item
}
func tableView(tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
if dropOperation == .Above {
return .Move
}
return .None
}
func tableView(tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItemsWithOptions([], forView: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
if let str = ($0.0.item as! NSPasteboardItem).stringForType("public.data"), index = Int(str) {
oldIndexes.append(index)
}
}
var oldIndexOffset = 0
var newIndexOffset = 0
// For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
// You may want to move rows in your content array and then call `tableView.reloadData()` instead.
tableView.beginUpdates()
for oldIndex in oldIndexes {
if oldIndex < row {
tableView.moveRowAtIndex(oldIndex + oldIndexOffset, toIndex: row - 1)
--oldIndexOffset
} else {
tableView.moveRowAtIndex(oldIndex, toIndex: row + newIndexOffset)
++newIndexOffset
}
}
tableView.endUpdates()
return true
}
Run Code Online (Sandbox Code Playgroud)
Swift 3版本:
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = NSPasteboardItem()
item.setString(String(row), forType: "private.table-row")
return item
}
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
}
return []
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
if let str = ($0.0.item as! NSPasteboardItem).string(forType: "private.table-row"), let index = Int(str) {
oldIndexes.append(index)
}
}
var oldIndexOffset = 0
var newIndexOffset = 0
// For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
// You may want to move rows in your content array and then call `tableView.reloadData()` instead.
tableView.beginUpdates()
for oldIndex in oldIndexes {
if oldIndex < row {
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
oldIndexOffset -= 1
} else {
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
newIndexOffset += 1
}
}
tableView.endUpdates()
return true
}
Run Code Online (Sandbox Code Playgroud)
Flu*_*imp 14
如果你看一下IB中的工具提示,你会看到你所指的选项
- (BOOL)allowsColumnReordering
Run Code Online (Sandbox Code Playgroud)
控制,以及列重新排序.除了用于表视图的标准拖放API之外,我不相信还有其他任何方法可以做到这一点.
编辑: (2012-11-25)
答案是指拖放重新排序NSTableViewColumns; 虽然这是当时公认的答案.现在差不多3年没有出现.为了使信息对搜索者有用,我将尝试给出更正确的答案.
没有允许NSTableView在Interface Builder中拖放行重新排序的设置.您需要实现某些NSTableViewDataSource方法,包括:
- tableView:acceptDrop:row:dropOperation:
- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
Run Code Online (Sandbox Code Playgroud)
还有其他的SO问题可以合理地解决这个问题,包括这个问题
Oly*_*tre 10
@ Ethan的解决方案 - 更新Swift 4
在viewDidLoad:
private var dragDropType = NSPasteboard.PasteboardType(rawValue: "private.table-row")
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
myTableView.registerForDraggedTypes([dragDropType])
}
Run Code Online (Sandbox Code Playgroud)
后来代表延期:
extension MyViewController: NSTableViewDelegate, NSTableViewDataSource {
// numerbOfRow and viewForTableColumn methods
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = NSPasteboardItem()
item.setString(String(row), forType: self.dragDropType)
return item
}
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
}
return []
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) { dragItem, _, _ in
if let str = (dragItem.item as! NSPasteboardItem).string(forType: self.dragDropType), let index = Int(str) {
oldIndexes.append(index)
}
}
var oldIndexOffset = 0
var newIndexOffset = 0
// For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
// You may want to move rows in your content array and then call `tableView.reloadData()` instead.
tableView.beginUpdates()
for oldIndex in oldIndexes {
if oldIndex < row {
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
oldIndexOffset -= 1
} else {
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
newIndexOffset += 1
}
}
tableView.endUpdates()
return true
}
}
Run Code Online (Sandbox Code Playgroud)
另外,对于那些可能关注的人:
如果你想禁用被dragable某些细胞,恢复nil在pasteboardWriterForRowS分析
如果你想防止掉落某个位置(例如太远),只需使用return []in validateDrop方法
不要在里面同步调用tableView.reloadData() func tableView(_ tableView:, acceptDrop info:, row:, dropOperation:).这将扰乱拖放动画,并且可能非常混乱.找到一种等待动画结束的方法,并异步重新加载
这个答案涵盖了Swift 3,基于视图的NSTableViews和单/多行拖放重新排序.
必须执行以下两个主要步骤才能实现此目的:
将表视图注册到可以拖动的特定允许类型的对象.
tableView.register(forDraggedTypes: ["SomeType"])
实施3种NSTableViewDataSource方法:writeRowsWith,validateDrop和acceptDrop.
在拖动操作开始之前IndexSet,在粘贴板中存储将要拖动的行的索引.
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
let data = NSKeyedArchiver.archivedData(withRootObject: rowIndexes)
pboard.declareTypes(["SomeType"], owner: self)
pboard.setData(data, forType: "SomeType")
return true
}
Run Code Online (Sandbox Code Playgroud)
仅当拖动操作高于指定行时才验证丢弃.这确保了在执行拖动时,当拖动的行将浮动在其上方时,其他行不会突出显示.此外,这解决了一个AutoLayout问题.
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int,
proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
} else {
return []
}
}
Run Code Online (Sandbox Code Playgroud)
当接受drop时,只检索IndexSet以前保存在粘贴板中的内容,迭代它并使用计算的索引移动行.
注意:迭代和行移动的部分我已经从@ Ethan的答案中复制了.
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
let pasteboard = info.draggingPasteboard()
let pasteboardData = pasteboard.data(forType: "SomeType")
if let pasteboardData = pasteboardData {
if let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: pasteboardData) as? IndexSet {
var oldIndexOffset = 0
var newIndexOffset = 0
for oldIndex in rowIndexes {
if oldIndex < row {
// Dont' forget to update model
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
oldIndexOffset -= 1
} else {
// Dont' forget to update model
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
newIndexOffset += 1
}
}
}
}
return true
}
Run Code Online (Sandbox Code Playgroud)
基于视图的NSTableViews在moveRow调用时更新自己,不需要使用beginUpdates()和endUpdates()阻止.
| 归档时间: |
|
| 查看次数: |
10094 次 |
| 最近记录: |