Swift中如何使用NSAnimationEffect实现拖拽移除NSTableView条目动画
NSTableView拖拽移除条目搭配NSAnimationEffect动画实现方案
核心逻辑:在拖拽结束的代理回调中先判断拖拽位置是否超出窗口边界,确认需要移除条目后,先获取目标条目的屏幕位置播放消散动画,动画结束后再更新数据源、删除TableView对应行即可。
API参数说明
你提到的NSShowAnimationEffect核心参数如下:
- 第一个参数:动画类型,移除条目的场景固定传
NSAnimationEffect.disappearingItemDefault(Swift)/NSAnimationEffectDisappearingItemDefault(OC),就是系统标准的条目消散效果,和工具栏移除图标动画完全一致 - 第二个参数:动画播放的屏幕中心点,需要把目标单元格的相对坐标转为屏幕坐标
- 第三个参数:动画的尺寸,直接用目标单元格的尺寸即可
- 第四个参数:关联的窗口,传当前TableView所在的window即可
- 最后两个参数:动画结束的回调,OC传代理和选择器,Swift可以直接用自带的闭包回调
代码示例
Swift实现
extension NSTableView { override open func draggingEnded(_ sender: NSDraggingInfo) { // 判断是否拖出窗口范围 guard let window = self.window, let contentView = window.contentView, !contentView.frame.contains(sender.draggingLocation) else { super.draggingEnded(sender) return } // 获取待移除的行索引 let targetRows = self.selectedRowIndexes guard let removeRow = targetRows.first else { return } // 计算单元格在屏幕上的中心点 let cellRect = self.rect(ofRow: removeRow) let cellWindowRect = self.convert(cellRect, to: nil) let animationCenter = NSPoint(x: cellWindowRect.midX, y: cellWindowRect.midY) // 播放移除动画 NSAnimationEffect.disappearingItemDefault.play( at: animationCenter, size: cellRect.size, for: window ) { [weak self] in guard let self = self else { return } // 动画结束后更新数据源和UI self.yourDataSourceArray.remove(at: removeRow) self.removeRows(at: IndexSet(integer: removeRow), withAnimation: .none) } } }
Objective-C实现
@implementation CustomTableView - (void)draggingEnded:(id<NSDraggingInfo>)sender { // 判断是否拖出窗口 NSWindow *window = self.window; NSPoint dragLocation = [sender draggingLocation]; if (NSPointInRect(dragLocation, window.contentView.frame)) { [super draggingEnded:sender]; return; } // 获取待移除行 NSIndexSet *selectedRows = self.selectedRowIndexes; if (selectedRows.count == 0) return; NSInteger removeRow = selectedRows.firstIndex; self.removeRowIndex = removeRow; // 存到成员变量供回调使用 // 计算动画位置 NSRect cellRect = [self rectOfRow:removeRow]; NSRect cellWindowRect = [self convertRect:cellRect toView:nil]; NSPoint animationCenter = NSMakePoint(NSMidX(cellWindowRect), NSMidY(cellWindowRect)); // 播放动画 NSShowAnimationEffect( NSAnimationEffectDisappearingItemDefault, animationCenter, cellRect.size, window, self, @selector(removeAnimationDidFinish) ); } // 动画结束回调 - (void)removeAnimationDidFinish { // 更新数据源删除行 [self.yourDataSourceArray removeObjectAtIndex:self.removeRowIndex]; [self removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:self.removeRowIndex] withAnimation:NSTableViewAnimationNone]; } @end
注意事项
- 必须等动画播放完成后再删除数据源和TableView行,否则单元格提前消失,会导致动画位置计算错误
- 多选拖拽移除的场景,可以遍历所有待移除行分别播放对应位置的动画,也可以统一在拖拽结束的鼠标位置播放动画
- 如果你需要自定义动画的消散轨迹,可以额外加CABasicAnimation配合实现,基础需求用系统自带的效果完全足够
内容的提问来源于stack exchange,提问作者unknown
相关产品推荐
相关产品推荐

