1. 概述

在iOS13及以后的版本,苹果将用UIContextMenuInteraction取代上文中提到的Peek和Pop的功能,Peek和Pop的功能需要依赖硬件设备,UIContextMenuInteraction则摆脱了对硬件的依赖。

在iOS9以及iPhone6s及以上的设备上,苹果推出了Peek和Pop功能,并在预览时上滑提供可供操作的操作菜单。在iOS13及以后,苹果禁用了UIViewControllerPreviewing协议相关方法,取而代之的则是UIContextMenuInteraction,如果项目最低运行版本是iOS13,且调用了UIViewControllerPreviewing协议相关方法,那么系统将会有黄色的警告,如果UIContextMenuInteraction和UIViewControllerPreviewing协议方法同时使用,系统只会采用UIContextMenuInteraction。

2. 上下文菜单(Context Menu)

在所有运行iOS13(及以上系统)的设备上,苹果都为其提供了上下文菜单功能,用户可以通过长按或者3D Touch(如果硬件支持)方式,弹出一个带可操作菜单的预览界面。
上下文菜单可以创建二级菜单,但是处于自动布局考虑,苹果还是建议所有菜单尽量在同一级别。
下面看一下系统相册列表长按图片的效果

图中,长按后弹出一个预览视图,底部带有操作选项列表,周边全部虚化,如果点击预览图,则可进入全屏界面查看图片。

2.1 一级菜单

要实现菜单的功能,则需要在控制器内实现UIContextMenuInteractionDelegate的协议方法:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration?

该方法为协议中必须实现的一个方法,方法要求返回一个UIContextMenuConfiguration对象。这个对象配置了Context menu所需要的预览图以及操作事件等。具体看一下:

属性或方法 说明
var identifier: NSCopying 初始化方法。
typealias UIContextMenuContentPreviewProvider 该block中返回预览视图控制器。
typealias UIContextMenuActionProvider 该block中返回可操作性的菜单。

举个例子,在一个界面中,如下图,按住button后弹出对图片的操作选项。

在代理回调中创建一个UIContextMenuConfiguration对象,并添加对应的Action事件,代码如下:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? inlet favoriteAction = UIAction(title: "喜欢", image: UIImage(systemName: "heart.fill"), state: .off) { (action) in}let shareAction = UIAction(title: "分享", image: UIImage(systemName: "square.and.arrow.up.fill"), state: .off) { (action) in}let deleteAction = UIAction(title: "删除", image: UIImage(systemName: "trash.fill"),attributes: [.destructive], state: .off) { (action) in}return UIMenu(title: "菜单", children: [favoriteAction, shareAction, deleteAction])}
}

然后还需要向button添加UIContextMenuInteraction对象,方可有长按住事件响应,代码如下:

let interaction = UIContextMenuInteraction(delegate: self)
button.addInteraction(interaction)

当按住button后,效果如下:

以上则将Context Menu的一级菜单显示出来了。
小结一下:

  • UIContextMenuInteraction: 添加一个context menu到指定的view。
  • UIContextMenuConfiguration: 配置一个带有action列表的对象给context menu。
  • UIContextMenuInteractionDelegate: 管理context menu的整个周期,弹出、展示、销毁。

2.2 二级菜单

二级菜单能够使Context Menu更加简洁、清晰明了。

下面在上面一级菜单的基础上增加一个打分功能二级菜单,代码如下:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? in// 二级action数组var ratingActions: Array<UIAction> = []// 遍历增加5个actionfor i in 0..<5 {let action = UIAction(title: "(i+1) 分") { (action) in}ratingActions.append(action)}// 创建一个打分的菜单let ratingMenu = UIMenu(title: "打分", image: UIImage(systemName: "star.circle"), children: ratingActions)let favoriteAction = UIAction(title: "喜欢", image: UIImage(systemName: "heart.fill"), state: .off) { (action) in}let shareAction = UIAction(title: "分享", image: UIImage(systemName: "square.and.arrow.up.fill"), state: .off) { (action) in}let deleteAction = UIAction(title: "删除", image: UIImage(systemName: "trash.fill"),attributes: [.destructive], state: .off) { (action) in}// 将打分的菜单放到一级菜单里面。return UIMenu(title: "菜单", children: [ratingMenu, favoriteAction, shareAction, deleteAction])}
}

运行效果图如下,当点击“打分”后,转入二级菜单界面。

除了二级菜单,还可以有三级、四级等菜单,写法就是层层嵌套,不过不建议搞这么多层的菜单,影响用户体验。

2.3 分组菜单

分组菜单可以将类似的功能归到一组,比如上面将删除单独归到一组,只需将删除的action包装到UIMenu中,并设置UIMenu的Options属性为displayInline即可,代码如下:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? in// 二级action数组var ratingActions: Array<UIMenuElement> = []// 遍历增加5个actionfor i in 0..<5 {let action = UIAction(title: "(i+1) 分") { (action) in}ratingActions.append(action)}// 创建一个打分的菜单let ratingMenu = UIMenu(title: "打分", image: UIImage(systemName: "star.circle"), children: ratingActions)let favoriteAction = UIAction(title: "喜欢", image: UIImage(systemName: "heart.fill"), state: .off) { (action) in}let shareAction = UIAction(title: "分享", image: UIImage(systemName: "square.and.arrow.up.fill"), state: .off) { (action) in}let deleteAction = UIAction(title: "删除", image: UIImage(systemName: "trash.fill"),attributes: [.destructive], state: .off) { (action) in}// 创建一个delete menu,然后设置options属性为displayInline,并将上面的deleteAction添加进来。let deleteMenu = UIMenu(title: "删除菜单", options: .displayInline, children: [deleteAction])// 将打分的菜单放到一级菜单里面。return UIMenu(title: "菜单", children: [ratingMenu, favoriteAction, shareAction, deleteMenu])}
}

运行效果图如下:

3. 预览视图及点击处理

预览视图可以快速的给用户提供一些预览信息。

上面的操作菜单是通过UIContextMenuConfiguration对象UIContextMenuActionProvider提供呢,那么预览视图则是通过UIContextMenuConfiguration对象的[UIContextMenuContentPreviewProvider](https://developer.apple.com/documentation/uikit/uicontextmenucontentpreviewprovider)提供的,同样是在创建UIContextMenuConfiguration对象的协议方法里面。

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil) { [weak self] () -> UIViewController? in// 创建预览视图,并返回。let detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)detailVC.image = self?.imageView.image// 预览视图显示大小detailVC.preferredContentSize = CGSize(width: 280, height: 360)return detailVC} actionProvider: { [weak self]  (elements) -> UIMenu? inreturn self?.createContextMenuActions()}
}

上面代码中创建了一个DetailViewController对象,并给其传递数据,用于预览内容,然后制定了预览图的大小。运行效果如下:

当点击底部action列表项的时候,自有对应的action block处理点击事件,那么如果点击预览图,如何处理呢?下面看这个协议方法:

/*!* 当用户点击预览图的时候调用,相当于3DTouch里面的Pop功能。** @param interaction    当前的交互对象* @param configuration  当前的配置项* @param animator       跳转动画执行者,可以给它添加跳转动画。*/
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)

下现在将这个协议方法具体实现以下:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {animator.addCompletion { [weak self] inif let this = self {let detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)detailVC.image = this.button.imageView?.imagethis.show(detailVC, sender: nil)}}
}

添加了上面的方法后,在运行点击预览图则会进入DetailViewController界面,全屏展示图片。

4. TableView中的Context Menu

TableView中的Context Menu,系统已经进行了封装,我们不需要在创建和添加UIContextMenuInteraction对象了,如果实现了协议方法,那么按住cell的时候就会响应协议方法。

相关协议方法如下:

func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)

Demo参考代码如下:

override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {let image = UIImage(named: imageArray[indexPath.row])return ImageContextMenuConfiguration.createInstance(indexPath.row, image)}override func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {if let index = Int(configuration.identifier as! String) {animator.addCompletion {[weak self]inlet detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)if let imageName = self?.imageArray[index] {detailVC.image = UIImage(named: imageName)}self?.show(detailVC, sender: nil)}}
}

5. CollectionView中的Context Menu

CollectionView中的Context Menu,系统已经进行了封装,我们不需要在创建和添加UIContextMenuInteraction对象了,如果实现了协议方法,那么按住cell的时候就会响应协议方法。

相关协议方法如下:

func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)

Demo参考代码如下:

override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {let image = UIImage(named: imageArray[indexPath.row])return ImageContextMenuConfiguration.createInstance(indexPath.row, image)}override func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {if let index = Int(configuration.identifier as! String) {animator.addCompletion {[weak self]inlet detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)if let imageName = self?.imageArray[index] {detailVC.image = UIImage(named: imageName)}self?.show(detailVC, sender: nil)}}
}

6. 总结

干巴巴的讲了一堆,如果不配上Demo,都有点说不过去,需要Demo的点击这里。
Context Menu主要用于预览和快速操作,功能和3D Touch差不多,但是有意取代3D Touch,以减少对硬件的依赖。

本文主要对该功能做了简单的说明,至于如何将该功能用的更好,还待日后具体研究了,文中如果有不对的地方,还请路过的朋友指正。

更多可操作的协议方法,详见**UIContextMenuInteractionDelegate。**
对了,苹果建议Context Menu采用系统提供的图片符号(SF Symbols),喜欢的小伙伴可以安装看看,链接为:developer.apple.com/sf-symbols/,根据自己电脑系统,下载对应的SF Symbols。

本篇文章出自blog.csdn.net/guoyongming…的博客,如需转载,请标明出处。

原文作者:Daniel_Coder
原文地址:https://blog.csdn.net/guoyongming925/article/details/110839969?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160766080119721940249352%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160766080119721940249352&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-12-110839969.pc_search_result_cache&utm_term=iOS%E5%BC%80%E5%8F%91&spm=1018.2118.3001.4449

底部菜单 点击突起_iOS开发之上下文交互菜单(UIContextMenuInteraction)相关推荐

  1. 微信自定义菜单java_Java微信公众平台开发(九)--微信自定义菜单的创建实现

    自定义菜单这个功能在我们普通的编辑模式下是可以直接在后台编辑的,但是一旦我们进入开发模式之后我们的自定义菜单就需要自己用代码实现,所以对于刚开始接触的人来说可能存在一定的疑惑,这里我说下平时我们在开发 ...

  2. iostext添加点击事件_iOS开发小技巧 - label中的文字添加点击事件

    Label中的文字添加点击事件 以前老师讲过类似的功能,自己懒得回头看了,找了很多第三方的,感觉这个小巧便利,作者只是扩展了分类,实现起来代码也少.先来个效果图 自己的项目,直接上代码 - (void ...

  3. cad自定义菜单cui_AutoCAD.NET二次开发:创建自定义菜单

    1.在项目中添加引用:AcCui.dll,这个DLL存放在CAD的安装目录中.但是,好像CAD2007之前的版本里没有这个DLL),我电脑上安装了CAD2004.CAD2005.CAD2008.CAD ...

  4. cad自定义菜单cui_AutoCAD.NET二次开发:创建自定义菜单(AcCui)

    从CAD2007之后,Autodesk提供了一个新的程序集AcCui.dll,使用这个程序集,我们可以方便地做一些界面方面的操作,比如创建自定义菜单. 下面介绍一下菜单的创建过程: 1.在项目中添加引 ...

  5. 微信公众平台开发教程Java版(六) 事件处理(菜单点击/关注/取消关注)

    前言: 事件处理是非常重要的,这一章讲讲常见的事件处理 1.关注/取消关注 2.菜单点击 事件类型介绍: 在微信中有事件请求是消息请求中的一种.请求类型为:event 而event事件类型又分多种事件 ...

  6. Chrome 插件开发-右键菜单开发实战演示,浏览器页面右键菜单选项设置,插件右键菜单点击插件名跳转主页设置

    Chrome 插件开发 - 菜单选项 浏览器页面右键菜单选项设置 ① 核心代码演示 ② 效果展示 ③ 详细参数文档 插件右键菜单点击插件名跳转主页设置 ① 核心代码演示 ② 演示效果图 浏览器页面右键 ...

  7. 微信公众号的二次开发(二 自定义菜单的创建)

    在上篇<微信公众号的二次开发(一)>我们介绍了微信订阅号的一些坑, 以及微信公众平台开发的一些基本配置.下面我们继续介绍如何通过用户关注等行为.获取用户的openid. 自定义微信公众号菜 ...

  8. Java微信公众平台开发(十)--微信自定义菜单的创建实现

    转自:http://www.cuiyongzhi.com/post/48.html 自定义菜单这个功能在我们普通的编辑模式下是可以直接在后台编辑的,但是一旦我们进入开发模式之后我们的自定义菜单就需要自 ...

  9. WPF 4 Ribbon 开发 之 应用程序菜单(Application Menu)

    原文:WPF 4 Ribbon 开发 之 应用程序菜单(Application Menu) 在上一篇中我们完成了快捷工具栏的开发,本篇将讲解应用程序菜单开发的相关内容.如下图所示,点击程序窗口左上角的 ...

最新文章

  1. Java基础语法纯小白入门
  2. 政府数据集中异地备份概述
  3. srs rtmp从监听到接收到新连接的过程分析
  4. 防水耐脏,超大容量双肩包,限时拼团仅需49元
  5. java 运行时修改类_在运行时修改类定义的注释字符串参数
  6. Android病毒家族及行为(一)
  7. JVM监控及诊断工具GUI篇之Arthas(二):jvm相关指令
  8. python-pygame作品之黑客帝国代码雨
  9. QPainter绘图基本使用
  10. 苹果双系统 计算机意外的重新启动,用Boot camp 安装双系统时突然重启,打… - Apple 社区...
  11. 短视频 SDK 架构设计实践
  12. android的app图标大全,安卓app图标
  13. 张一鸣打造“抖音”等现象级产品的秘密:技术深度融合业务,引爆产品创新!...
  14. [VS2010]逸雨清风 校园网视频,网吧视频(光音网视)下载器 V0.26
  15. python实现爬取非小号相关性(btc)数据
  16. http 请求头、响应头、请求体的各个属性的含义
  17. 谁有粉?就爬谁!他粉多,就爬他!Python 多线程采集 260000+ 粉丝数据
  18. Squid TCP_MISS/000 的意义
  19. windows10局域网设置固定IP地址
  20. NXP S32K1 时钟系统

热门文章

  1. makefile中模式规则的引入和介绍------%:%.cpp
  2. linux工具之检测内存泄漏-valgrind
  3. Flask实战2问答平台--导航条
  4. 二叉树剪枝_决策树,生成剪枝,CART算法
  5. qt 进度条_Qt开源作品12-硬盘容量控件
  6. JavaScript原型链的理解
  7. 计算机网络-数据链路层
  8. ssh项目实战----Spring计时器任务 Spring整合JavaMail(邮件发送)
  9. 数据中心智能电源分配器(PDU)的趋势与应用
  10. docker与k8s面试题基础