iOS开发之UIMenuController使用示例详解

目录
  • 简介
  • 接口介绍
  • 使用探索
    • 如何创建并显示 UIMenuController
    • 实现 Item 点击事件
    • 菜单 Item 太多???
    • UIResponderStandardEditActions 协议
    • 添加自定义菜单
    • 箭头的方向
  • 实际使用
  • 总结

简介

UIMenuController 是一个菜单编辑界面,在很多地方都能用到,通常用于剪切、复制、粘贴、选择、全选和删除命令等,也可以自定义想要的操作,它长这样:

接口介绍

open class UIMenuController : NSObject {
    open class var shared: UIMenuController { get }
    open var isMenuVisible: Bool // default is NO
    @available(iOS, introduced: 3.0, deprecated: 13.0, message: "Use showMenuFromView:rect: or hideMenuFromView: instead.")
    open func setMenuVisible(_ menuVisible: Bool, animated: Bool)
    @available(iOS, introduced: 3.0, deprecated: 13.0, message: "Use showMenuFromView:rect: instead.")
    open func setTargetRect(_ targetRect: CGRect, in targetView: UIView)
    @available(iOS 13.0, *)
    open func showMenu(from targetView: UIView, rect targetRect: CGRect)
    @available(iOS 13.0, *)
    open func hideMenu(from targetView: UIView)
    @available(iOS 13.0, *)
    open func hideMenu()
    @available(iOS 3.2, *)
    open var arrowDirection: UIMenuController.ArrowDirection // default is UIMenuControllerArrowDefault
    @available(iOS 3.2, *)
    open var menuItems: [UIMenuItem]? // default is nil. these are in addition to the standard items
    open func update()
    open var menuFrame: CGRect { get }
}
open class UIMenuItem : NSObject {
    public init(title: String, action: Selector)
    open var title: String
    open var action: Selector
}

从接口中可以看出 UIMenuController 应该使用它的单例对象,具体应该怎么使用它呢?我们先来看一下 API 文档对 UIMenuController 的说明:

The singleton UIMenuController instance is referred to as the editing menu. When you make this menu visible, UIMenuController positions it relative to a target rectangle on the screen; this rectangle usually defines a selection. The menu appears above the target rectangle or, if there is not enough space for it, below it. The menu’s pointer is placed at the center of the top or bottom of the target rectangle, as appropriate. Be sure to set the tracking rectangle before you make the menu visible. You are also responsible for detecting, tracking, and displaying selections.

The UIResponderStandardEditActions informal protocol declares methods that are invoked when the user taps a menu command. The canPerformAction(_:withSender:) method of UIResponder is also related to the editing menu. A responder implements this method to enable and disable commands of the editing menu just before the menu is displayed. You can force this updating of menu commands’ enabled state by calling the update() method.

You can also provide your own menu items via the menuItems property. When you modify the menu items, you can use the update() method to force the menu to update its display.

翻译如下:

UIMenuController 单例称为编辑菜单。当你使这个菜单可见时,UIMenuController 将它相对于屏幕上的目标矩形定位;这个矩形通常定义一个选择。菜单显示在目标矩形上方,如果没有足够的空间,则显示在其下方。菜单指针放置在目标矩形顶部或底部的中心,视情况而定。确保在使菜单可见之前设置跟踪矩形。您还负责检测、跟踪和显示选择。

UIResponderStandardEditActions 协议声明了在用户点击菜单命令时调用的方法。 UIResponder 的 canPerformAction(_:withSender:) 方法也和编辑菜单有关。响应者实现此方法以在菜单显示之前启用和禁用编辑菜单的命令。您可以通过调用 update() 方法强制更新菜单命令的启用状态。

您还可以通过 menuItems 属性提供您自己的菜单项。修改菜单项时,可以使用 update() 方法强制菜单更新其显示。

使用探索

根据 API 说明可知

  • UIMenuController 显示位置可以通过设置一个矩形来定位
  • 要想显示 UIMenuController,需要成为响应者
  • 如果没有设置 menuItems 时有自己默认的菜单,也可以通过 menuItems 添加自己的菜单

如何创建并显示 UIMenuController

首先,API 说的很清楚,UIMenuController 是单例,直接使用 UIMenuController.shared 即可,然后调用 open func showMenu(from targetView: UIView, rect targetRect: CGRect) 方法来显示,

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let menu = UIMenuController.shared
    menu.showMenu(from: view, rect: CGRect(x: 50, y: 50, width: 20, height: 20))
}

运行代码发现并没有什么反应,回看 API,还需要设置第一响应者

override var canBecomeFirstResponder: Bool {
    true
}
// 上文提到的其他代码忽略

运行代码还是没反应,回看 API,UIResponder 的 canPerformAction(_:withSender:) 方法也和编辑菜单有关。响应者实现此方法以在菜单显示之前启用和禁用编辑菜单的命令,我们实现一下试试

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    true
}
// 上文提到的其他代码忽略

当当当当,成功了!!!

实现 Item 点击事件

接下来,我鼠标轻轻的点在了菜单上 Cut,结果奔溃了:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SwiftTestiOS.ViewController cut:]: unrecognized selector sent to instance 0x7fec7300d480'

根据提示,我们实现 cut 方法。

override func cut(_ sender: Any?) {
    print("cut cut cut !!!")
}
// 上文提到的其他代码忽略

nice,没有奔溃,成功打印 cut cut cut !!!

其他的菜单也可以添加对应的实现,哈哈哈...搞定!!!

菜单 Item 太多???

问题:我不需要这么多菜单,咋整?

之前没有因为没有实现 canPerformAction(_:withSender:) 方法时,UIMenuController 无法出现,实现了之后就出现了一大堆菜单,此方法有一个action参数,是不是此方法决定了哪些action可以显示呢,试试看:

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    let actions = [#selector(cut(_:)), #selector(copy(_:)),
                   #selector(paste(_:)), #selector(delete(_:))]
    return actions.contains(action)
}
// 上文提到的其他代码忽略

也就是说,canPerformAction(_:withSender:) 决定设置的哪些菜单可以生效。敲黑板,这点很重要!!!

UIResponderStandardEditActions 协议

根据 API 说明,UIResponderStandardEditActions 协议定义了 UIMenuController 的一些系统默认方法,内容如下

public protocol UIResponderStandardEditActions : NSObjectProtocol {
    @available(iOS 3.0, *)
    optional func cut(_ sender: Any?)
    @available(iOS 3.0, *)
    optional func copy(_ sender: Any?)
    @available(iOS 3.0, *)
    optional func paste(_ sender: Any?)
    @available(iOS 3.0, *)
    optional func select(_ sender: Any?)
    @available(iOS 3.0, *)
    optional func selectAll(_ sender: Any?)
    @available(iOS 3.2, *)
    optional func delete(_ sender: Any?)
    //... 其他方法略
}

而且上文实现 cut 方法的时候有override,也就是 UIViewController 有这个方法,根据线索可以查到对应关系

UIViewController

UIResponder

UIResponderStandardEditActions

添加自定义菜单

如果系统提供的菜单不满足我们自己的需求,可以通过 menuItems 添加自定义菜单

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let menu = UIMenuController.shared
    let item1 = UIMenuItem(title: "hello", action: #selector(helloAction))
    let item2 = UIMenuItem(title: "world", action: #selector(worldAction))
    menu.menuItems = [item1, item2]
    menu.showMenu(from: view, rect: CGRect(x: 50, y: 50, width: 20, height: 20))
}
@objc func helloAction() {
    print(#function)
}
@objc func worldAction() {
    print(#function)
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    let actions = [#selector(cut(_:)), #selector(copy(_:)),
                   #selector(paste(_:)), #selector(delete(_:)),
                   #selector(helloAction), #selector(worldAction)]
    return actions.contains(action)
}
// 上文提到的其他代码忽略

添加之后效果如下:

箭头的方向

UIMenuController 有个arrowDirection 属性,用于设置箭头的位置,它是ArrowDirection 类型的枚举

extension UIMenuController {
    public enum ArrowDirection : Int, @unchecked Sendable {
        case `default` = 0
        case up = 1
        case down = 2
        case left = 3
        case right = 4
    }
}

默认的时候,会根据需要调整箭头方向,而箭头的位置根据 API 描述(菜单指针放置在目标矩形顶部或底部的中心,视情况而定)是在设置的矩形区域的上下边的中间位置。

注意:如果强制设定了一个方向的话,而在该方向没有足够的空间,则不会显示菜单

实际使用

在显示 UIMenuController 的时候有一个方法 open func showMenu(from targetView: UIView, rect targetRect: CGRect),此方法主要是用来设置显示位置的,targetView 指明位置参照对象,rect 表示参照 targetView 的位置,如:

let position = label.bounds
menu.showMenu(from: label, rect: position)

上述代码可以理解成:菜单显示在相对于 label 的 position 处

总结

UIMenuController 的使用总体还是比较简单的,主要是以下几点:

UIMenuController 是单例

显示默认的 UIMenuController 菜单需要

  • 成为第一响应者
  • UIResponder 的 canPerformAction(_:withSender:)返回可以添加的 UIMenuItem
  • 实现对应 UIMenuItem 的方法

自定义 UIMenuItem

  • 通过 UIMenuController.menuItems 属性添加自定义的 UIMenuItem
  • 在 canPerformAction(_:withSender:) 对应 UIMenuItem 的 action

箭头的位置

  • 建议使用 ArrowDirection.default
  • 使用其他值可能会看不见菜单,除非确定一定可以显示,否则不推荐使用

以上就是iOS开发之UIMenuController使用示例详解的详细内容,更多关于iOS开发UIMenuController的资料请关注我们其它相关文章!

(0)

相关推荐

  • iOS中长按调出菜单组件UIMenuController的使用实例

    UIMenuController的使用 UIMenuController的展现需要基于一个View视图,其交互则需要基于其所在View视图的Responder.举例来说,如果一个UIMenuController展现在当前ViewController的View上,则此UIMenuController的交互逻辑交由当前的ViewController进行管理. 在界面展示出UIMenuController需要3个条件: 1.当前的Responder处于第一响应. 2.UIMenuController对

  • UIMenuController在Cell内部无法显示的解决办法(iOS9.2)

    Xcode7.2,iOS9.2环境下 尝试在CollectionViewCell内部,添加LongPress手势,显示UIMenuController. @implementation CollectionViewCell //继承自UICollectionViewCell UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@sele

  • iOS开发之UIMenuController使用示例详解

    目录 简介 接口介绍 使用探索 如何创建并显示 UIMenuController 实现 Item 点击事件 菜单 Item 太多??? UIResponderStandardEditActions 协议 添加自定义菜单 箭头的方向 实际使用 总结 简介 UIMenuController 是一个菜单编辑界面,在很多地方都能用到,通常用于剪切.复制.粘贴.选择.全选和删除命令等,也可以自定义想要的操作,它长这样: 接口介绍 open class UIMenuController : NSObject

  • iOS开发之UIScrollView控件详解

    一.UIScrollView控件是什么? (1)移动设备的屏幕⼤大⼩小是极其有限的,因此直接展⽰示在⽤用户眼前的内容也相当有限 (2)当展⽰示的内容较多,超出⼀一个屏幕时,⽤用户可通过滚动⼿手势来查看屏幕以外的内容 (3)普通的UIView不具备滚动功能,不能显⽰示过多的内容 (4)UIScrollView是一个能够滚动的视图控件,可以⽤用来展⽰示⼤大量的内容,并且可以通过滚 动查看所有的内容 (5)  举例:手机上的"设置".其他⽰示例程序 二.UIScrollView的简单使用 (

  • iOS开发之AssetsLibrary框架使用详解

    一.引言 AssetsLibrary框架是专门用来操作相册相关资源的一个框架,其是iOS4到iOS9之间常使用的一个框架,在iOS9之后,系统系统了Photos框架代替了AssetsLibrary框架,但是AssetsLibrary框架依然可以使用,并且其结构和设计思路依然值得我们进行分析学习. 二.概述 AssetsLibrary框架会操作系统的相册,因此首先需要进行权限的申请,在使用之前,首先需要在Info.plist文件中添加如下键值: Privacy - Photo Library Us

  • 移动web开发之touch事件实例详解

    前面的话 iOS版Safari为了向开发人员传达一些特殊信息,新增了一些专有事件.因为iOS设备既没有鼠标也没有键盘,所以在为移动Safari开发交互性网页时,常规的鼠标和键盘事件根本不够用.随着Android 中的WebKit的加入,很多这样的专有事件变成了事实标准,导致W3C开始制定Touch Events规范.本文将详细介绍移动端touch事件 概述 包含iOS 2.0软件的iPhone 3G发布时,也包含了一个新版本的Safari浏览器.这款新的移动Safari提供了一些与触摸(touc

  • JSP 开发之 releaseSession的实例详解

    JSP 开发之 releaseSession的实例详解 Hibernate可以实现分页查询,昨天试了一下,分页效果不错.但是发现了一个问题,就是当请求超过20次的时候页面就会卡死.经检查,是卡在分页查询这一块. 应用程序采用struts2 + spring2 + hibernate3架构 连接池配置使用的是c3p0, 最大池大小为20, 很显然是连接池耗尽导致的. 增加连接池大小只是饮鸩止渴,总还有耗尽的时候,必须找到根本原因. Dao类的分页查询方法如下: java 代码  public Li

  • Android音频开发之SurfaceView的使用详解

    目录 SurfaceView 不同点 双缓冲机制 SurfaceHolder 使用 SurfaceView SurfaceView从源码上看继承自View,但在内部实现上SurfaceView和其他View有很多区别. SurfaceView主要作用是提供一个直接绘图表面嵌入到视图结构中,实际上真正做绘制能力的是Surface.因此SurfaceView和宿主窗口是分离的.正常情况下窗口的View共享同一个Window,而Window也对应一个Surface,所有View也就共享同一个Surfa

  • C#面向对象编程中开闭原则的示例详解

    目录 开闭原则 C# 示例 改进 总结 在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原则的子集,在他2000年的论文<设计原则与设计模式>中首次提出. SOLID 原则包含: S:单一功能原则(single-responsibility principle) O:开闭原则(open-closed principle) L:里氏替换原则(Lis

  • iOS封装倒计时按钮HLCountDownButton示例详解

    目录 引言 演示图 可选自定义属性 3种样式选择 回调说明 全局样式设置 引言 在开发中经常会用到倒计时的按钮,常用在发送短信验证码中.最差的写法是把代码写在ViewController中.这样的话如果项目中存在多个倒计时按钮,代码就十分臃肿了.所以封装一个倒计时按钮就迫在眉睫了.传送门HLCountDownButton,欢迎帮忙点点小星星 支持过xib.storyboard自定义样式 支持切换前后台计时准确 多种样式切换 支持CocoaPods导入pod "HLCountDownButton&

  • 安卓开发之FragmentPagerAdapter和FragmentStatePagerAdapter详解

    目录 FragmentPagerAdapter与FragmentPagerStateAdapter区别点: 一:二者在状态保存有差异:FragmentPagerAdapter并未实现saveState().restoreState() 二:二者在视图管理方法差异: 最近遇到比较奇怪的bug,TableLayout+ViewPager实现点击顶部tab切换viewpager视图.但是在Viewpager设置dapter时,最开始设置的是FragmentPagerAdapter,会导致tab切换后F

  • IOS开发Objective-C Runtime使用示例详解

    目录 前言 一些关键字 消息传递 (Messaging) KVO 关联对象 (Associated Objects) AOP(Method Swizzling) 其它 前言 Runtime 是使用 C 和汇编实现的运行时代码库,Objective-C 中有很多语言特性都是通过它来实现.了解 Runtime 开发可以帮助我们更灵活的使用 Objective-C 这门语言,我们可以将程序功能推迟到运行时再去决定怎么做,还可以利用 Runtime 来解决项目开发中的一些设计和技术问题,使开发过程更加具

随机推荐