21. Drag-Drop拖放操作(二) - 文件、表格和树的拖放实现

本了继上节内容,讲述几种常用的拖放场景示例,包括文件、表格和树的拖放实现。

文件拖放

实现从系统目录拖放文件到App中。
在这里插入图片描述

自定义接收视图

自定义应用内部接收拖放的view视图类FileDragView,注册拖放类型,实现目标拖放协议NSDraggingDestination。

//自定义拖放类型
public let kImagePboardTypeName = "macdev.io.fileDrag"
let NSFilenamesPboardType = NSPasteboard.PasteboardType(rawValue: kImagePboardTypeName)//文件拖放应用代理协议
protocol FileDragDelegate: class {func didFinishDrag(_ filePath:String)
}class FileDragView: NSView {weak var delegate: FileDragDelegate?override func awakeFromNib() {super.awakeFromNib()//注册文件拖放类型self.registerForDraggedTypes([NSFilenamesPboardType])}//MARK: NSDraggingDestination//开始拖放,返回拖放类型override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {let sourceDragMask = sender.draggingSourceOperationMask()let pboard = sender.draggingPasteboard()let dragTypes = pboard.types! as NSArrayif dragTypes.contains(NSFilenamesPboardType) {if sourceDragMask.contains([.link]) {return .link}if sourceDragMask.contains([.copy]) {return .copy}}return .generic}//拖放文件进入拖放区,返回拖放操作类型override func performDragOperation(_ sender: NSDraggingInfo?)-> Bool {let pboard = sender?.draggingPasteboard()let dragTypes = pboard!.types! as NSArrayif dragTypes.contains(NSFilenamesPboardType) {let files = (pboard?.propertyList(forType: NSFilenamesPboardType))! as!  Array<String>let numberOfFiles = files.countif numberOfFiles > 0 {let filePath = files[0] as String//代理通知if let delegate = self.delegate {NSLog("filePath \(filePath)")delegate.didFinishDrag(filePath)}}}return true}
}

实现拖放响应处理

文件拖放响应处理

class ViewController: NSViewController {//文本框@IBOutlet var textView: NSTextView!//自定义的此controller中的view@IBOutlet var dragView: FileDragView!override func viewDidLoad() {super.viewDidLoad()//设置代理self.dragView.delegate = self}
}//实现代理方法
extension ViewController: FileDragDelegate {func didFinishDrag(_ filePath:String) {let url = NSURL(fileURLWithPath: filePath)guard let string = try?  NSString.init(contentsOf: url as URL, encoding: String.Encoding.utf8.rawValue)else {return}self.textView.string = string as String}
}

表格行数据拖放

在UI中添加一个表格,下面的行是可拖动的。
在这里插入图片描述

初始化表格

设置NSTableColumn的identifier

按下图设置
在这里插入图片描述

设置NSTableView的Delegate和DataSource

拖动右侧+号到左侧UI导航栏的View Controller
在这里插入图片描述

行拖动实现

这里把表格初始化和行拖动混在一上进心实现,最好是单独实现一个类。

import Cocoa//定义拖放事件类型
let kTableViewDragDataTypeName = "TableViewDragDataTypeName"
let tableViewDragDataTypeName = NSPasteboard.PasteboardType(rawValue: kTableViewDragDataTypeName)class ViewController: NSViewController {@IBOutlet weak var tableView: NSTableView!var datas = [NSDictionary]()override func viewDidLoad() {super.viewDidLoad()//更新数据self.updateData()//注册行拖动类型self.tableView.registerForDraggedTypes([tableViewDragDataTypeName])}func updateData() {self.datas = [["name":"john","address":"USA"],["name":"mary","address":"China"],["name":"park","address":"Japan"],["name":"Daba","address":"Russia"],]}
}extension ViewController: NSTableViewDataSource {func numberOfRows(in tableView: NSTableView) -> Int {return self.datas.count}//数据复制到剪切板func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {let zNSIndexSetData = NSKeyedArchiver.archivedData(withRootObject: rowIndexes);pboard.declareTypes([tableViewDragDataTypeName], owner: self)pboard.setData(zNSIndexSetData, forType: tableViewDragDataTypeName)return true}//返回允许拖动的操作类型func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {return .every}//拖放数据处理func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {let pboard = info.draggingPasteboard()let rowData = pboard.data(forType: tableViewDragDataTypeName)let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: rowData!) as! NSIndexSetlet dragRow = rowIndexes.firstIndexlet temp = self.datas[row]self.datas[row] = self.datas[dragRow]self.datas[dragRow] = temptableView.reloadData()return true}
}extension ViewController: NSTableViewDelegate {func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {let data = self.datas[row]//表格列的标识let key = (tableColumn?.identifier)!//单元格数据let value = data[key]//根据表格列的标识,创建单元视图let view = tableView.makeView(withIdentifier: key, owner: self)let subviews = view?.subviewsif (subviews?.count)!<=0 {return nil}let textField = subviews?[0] as! NSTextFieldif value != nil {textField.stringValue = value as! String}return view}
}

树节点数据拖放

树本质上是一种表格实现,只不过只有一列,所以其初始化方式同表格相同。
在这里插入图片描述

初始化

代码如下:

import Cocoa// 自定义数据拖放类型
let kDragOutlineViewTypeName = "kDragOutlineViewTypeName"
let dragOutlineViewTypeName = NSPasteboard.PasteboardType(rawValue: kDragOutlineViewTypeName)class ViewController: NSViewController {lazy var nodes: Array = {return [NSMutableDictionary]()}()//表格视图@IBOutlet weak var treeView: NSOutlineView!override func viewDidLoad() {super.viewDidLoad()self.configData()self.registerDrag()self.treeView.reloadData()self.treeView.expandItem(nil, expandChildren: false)}//注册拖放func registerDrag() {self.treeView.registerForDraggedTypes([dragOutlineViewTypeName])}//配置测试数据func configData() {let group1 = NSMutableDictionary()let group2 = NSMutableDictionary()group1["name"] = "Group1"group2["name"] = "Group2"let member11 = NSMutableDictionary()member11["name"] = "member11"let member12 = NSMutableDictionary()member12["name"] = "member12"let children1 = [ member11,member12]let member21 = NSMutableDictionary()member21["name"] = "member21"let member22 = NSMutableDictionary()member22["name"] = "member22"let member23 = NSMutableDictionary()member23["name"] = "member23"let children2 = [member21,member22,member23]group1["children"] = NSMutableArray(array: children1)group2["children"] = NSMutableArray(array: children2)self.nodes.append(group1)self.nodes.append(group2)}
}

实现协议

实现相关协议,这个协议代码写在哪里,与Viewxdpt的Controller有关。

extension ViewController:NSOutlineViewDataSource {func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {if(item == nil) {return self.nodes.count}let node = item as! NSMutableDictionaryif let children = node["children"] as? NSArray   {return children.count}return 0}func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {if(item==nil) {return  self.nodes[index]}let node = item as! NSMutableDictionarylet children = node["children"] as! NSArrayreturn  children[index]}func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {return true}//将拖放的节点数据 存储到剪切板func outlineView(_ outlineView: NSOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool {if  items.count == 0 {return false}let data = NSKeyedArchiver.archivedData(withRootObject: items)pasteboard.declareTypes([dragOutlineViewTypeName], owner: self)pasteboard.setData(data, forType: dragOutlineViewTypeName)return true}//节点允许拖放func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {return .every}//拖放数据接收处理func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {let pboard = info.draggingPasteboard()let data = pboard.data(forType: dragOutlineViewTypeName)let items = NSKeyedUnarchiver.unarchiveObject(with: data!)guard let itemsData = items else {return false}let targetNodeItem = item as! NSMutableDictionaryvar children = targetNodeItem["children"] as? NSMutableArray//如果拖放的目标节点没有孩子,则创建一个新孩子节点if children == nil {children =  NSMutableArray()targetNodeItem["children"] = children}//将数据添加到目标节点的children中children?.addObjects(from: itemsData as! [AnyObject])//重新加载数据outlineView.reloadData()return true}
}extension ViewController: NSOutlineViewDelegate {func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {let view = outlineView.makeView(withIdentifier: (tableColumn?.identifier)!, owner: self)let subviews  = view?.subviewslet field = subviews?[0] as! NSTextFieldlet node = item as! NSMutableDictionaryif let name = node["name"] {field.stringValue = name as! String}return view}func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {return 20}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470788.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣515:在每个树行中找最大值

给定一棵二叉树的根节点 root &#xff0c;请找出该二叉树中每一层的最大值。 示例1&#xff1a; 输入: root [1,3,2,5,3,null,9] 输出: [1,3,9]示例2&#xff1a; 输入: root [1,2,3] 输出: [1,3]提示&#xff1a; 二叉树的节点个数的范围是 [0,104]-231 < Node.val &l…

vivo 游戏中心包体积优化方案与实践

作者&#xff1a;来自 vivo 互联网大前端团队- Ke Jie 介绍 App 包体积优化的必要性&#xff0c;游戏中心 App 在实际优化过程中的有效措施&#xff0c;包括一些优化建议以及优化思路。 一、包体积优化的必要性 安装包大小与下载转化率的关系大致是成反比的&#xff0c;即安装…

数据库SQL——连接表达式(JOIN)图解

目录 一、基本概念 二、常见类型 内连接&#xff08;INNER JOIN&#xff09;&#xff1a; 左连接&#xff08;LEFT JOIN 或 LEFT OUTER JOIN&#xff09;&#xff1a; 右连接&#xff08;RIGHT JOIN 或 RIGHT OUTER JOIN&#xff09;&#xff1a; 全连接&#xff08;FULL…

Sigrity SPEED2000 Power Ground Noise Simulation模式如何查看PDS系统的自阻抗操作指导

Sigrity SPEED2000 Power Ground Noise Simulation模式如何查看PDS系统的自阻抗操作指导 Sigrity Power SI Power Ground Noise Simulation模式可以用于PDS系统自阻抗分析,以下图为例 2D视图

uni-app移动端与PC端兼容预览PDF文件

过程遇到的问题 1、如果用的是最新的版本的pdfjs的话&#xff0c;就会报Promise.withResolvers 不是一个方法的错误&#xff0c;原因是Promise.withResolvers是ES15新特性&#xff0c;想了解可参考链接&#xff0c;这里的解决方案是将插件里的涉及到Promise.withResolvers的地…

「Py」Python基础篇 之 Python都可以做哪些自动化?

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「Py」Python程序设计&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定…

[ 网络安全介绍 5 ] 为什么要学习网络安全?

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战

目录 前言 一、原始的处理办法 1、使用Set方法来转换 2、使用构造方法转换 二、基于ModelMapper的动态转换 1、ModelMapper简介 2、集成到项目中 3、Shapefile属性读取 三、总结 前言 在现代软件开发中&#xff0c;尤其是在多层架构中&#xff0c;经常需要将数据从一个…

时间管理的三个痛点

时间管理方面&#xff0c;有三个痛点&#xff1a;不知道、不平衡、不安全。 很多人&#xff0c;忙了一天&#xff0c;感觉很累&#xff0c;但是不知道做了什么。他不知道&#xff0c;这一天工作了几个小时&#xff0c;做了哪些事&#xff0c;分别用了多少时间&#xff0c;只是…

封装el-menu

案例图 数据格式 commonMenu.vue <template><div class"commonMenuStyle"><el-sub-menu v-if"hasChildren" :index"item.MenuId"><template #title><el-icon><location /></el-icon><!-- isColl…

微服务day07

Elasticsearch 需要安装elasticsearch和Kibana&#xff0c;应为Kibana中有一套控制台可以方便的进行操作。 安装elasticsearch 使用docker命令安装&#xff1a; docker run -d \ --name es \-e "ES_JAVA_OPTS-Xms512m -Xmx512m" \ //设置他的运行内存空间&#x…

假期增设:福祉与负担并存,寻求生活经济平衡之道

近年来&#xff0c;关于春节与五一假期各自增设一日的议题持续引发广泛热议。这一额外假期的增设&#xff0c;究竟是民众福祉的增益&#xff0c;还是社会运行的额外负担&#xff0c;值得我们深入探讨。 从宏观经济视角审视&#xff0c;假期的延长产生了复杂而深远的影响。一方面…

结构体(c语言)

一.结构体 1.结构的基础知识 结构是一些值得集合&#xff0c;这些值称为成员变量&#xff0c;结构得每个成员可以是不同类型的变量 2.结构体的声明 struct tag//结构体名称{member-list;//成员变量}variable-list;//全局变量 例&#xff1a;描述一个学生 struct Stu {int a…

GIS空间分析案例---城市公共设施配置与服务评价

今天给大家带来新的GIS案例分析——“城市公共设施配置与服务评价” 数据准备 本案例提供的数据资料如下&#xff1a; 武汉城区.shp&#xff1a;武汉市三环城区范围面要素&#xff1b; 武汉城区&#xff08;去除水系&#xff09;.shp&#xff1a;去除水系后的研究区范围面要…

如何在算家云搭建Peach-9B-8k-Roleplay(文本生成)

一、Peach-9B-8k-Roleplay简介 Peach-9B-8k-Roleplay 是一种聊天大型语言模型&#xff0c;它是通过我们的数据合成方法创建的超过 100K 的对话中微调 01-ai/Yi-1.5-9B 模型而获得的。 也许是 34B 以下参数最好的 LLM。 二、模型搭建流程 1. 创建容器镜像 进入算家云平台的“…

软件工程概论项目(二),node.js的配置,npm的使用与vue的安装

上一章我们配置了git仓库&#xff0c;这一章我们来配置项目需要用的一些其他的环境。 放一个思维导图在这里&#xff0c;可以参考一下&#xff0c;很不全面&#xff0c;没有参考价值,反正我先这样写吧。 参考了这个nodejs的配置&#xff0c;写的很好&#xff1a;https://blog.c…

Android中桌面小部件的开发流程及常见问题和解决方案

在Android中&#xff0c;桌面小部件&#xff08;App Widget&#xff09;是应用程序可以在主屏幕或其他地方显示的一个可视化组件&#xff0c;提供简化信息和交互功能。Android桌面小部件的framework为开发者提供了接口&#xff0c;使得可以创建和更新小部件的内容。以下是Andro…

JAVA题目笔记(十五)经典算法题

一、按要求排序 要求&#xff1a;定义数组并存储一些女朋友对象&#xff0c;利用Arrays中的sort方法进行排序 属性包括&#xff1a;姓名&#xff0c;年龄&#xff0c;身高 按照年龄大小进行排序&#xff0c;年龄一样按照身高排序&#xff0c;身高一样按照姓名字母进行排序。…

【JVM】关于JVM的内部原理你到底了解多少(八股文面经知识点)

前言 &#x1f31f;&#x1f31f;本期讲解关于HTTPS的重要的加密原理~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

【Pikachu】目录遍历实战

既然已经决定做一件事&#xff0c;那么除了当初决定做这件事的我之外&#xff0c;没人可以叫我傻瓜。 1.目录遍历漏洞概述 目录遍历漏洞概述 在Web功能的设计过程中&#xff0c;开发者经常会将需要访问的文件作为变量进行定义&#xff0c;以实现前端功能的灵活性。当用户发起…