You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 19:15:03