Swift: 给 Label 添加 UIMenuControll
2017-05-18 本文已影响136人
婉卿容若
功能
给 Label 添加复制功能并以 UIMenuController 显示展示
为什么
需要使用, 尽管很简单...
OC 时弄过, 但是用 Swift 写了一次, UIMenuController 一直显示失败..
看了别人写的笔记...发现都有问题
版本
基于 Swift3+
步骤 - 这是一个继承了 UILabel 的 label 控件
- 给 Label 打开交互响应
self.isUserInteractionEnabled = true
- 给 Label 添加长按手势(具体什么手势自己根据需求)
let longPressGesture = UILongPressGestureRecognizer.init(target: self, action: #selector(longPressAction(gesture:)))
self.addGestureRecognizer(longPressGesture)
手势事件
func longPressAction(gesture: UILongPressGestureRecognizer){
if gesture.state == .began{
self.becomeFirstResponder()
//let copyItem = UIMenuItem(title: "复制", action: #selector(copy(_:)))
let menuVC = UIMenuController.shared
// menuVC.menuItems = [copyItem]
if menuVC.isMenuVisible {
return
}
menuVC.setTargetRect(bounds, in: self)
menuVC.setMenuVisible(true, animated: true)
}
}
说明:
-
self.becomeFirstResponder()
必须调用...但是 label 是否可以获取焦点是由canBecomeFirstResponder属性决定的, 它的默认值是 NO. 所以我们需求重写它.
OC 中我们重写这个属性的 set 方法即可:
- (BOOL)canBecomeFirstResponder{
return YES;
}```
Swift 中这个属性是一个可读的计算属性.
看很多人的笔记是这个重写的:
``` override func canBecomeFirstResponder() -> Bool {
return true
}
这种写法会报错...可能当时他们用的 Swift 版本允许
现在应该这个写:
override var canBecomeFirstResponder: Bool {
return true
}
不要试图给它复制...这是个只读属性
- 注意判断长按手势的状态
gesture.state == .began
,否则执行两次相关当妈
- 还必须要重写这个方法
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)){
return true
}else{
return false
}
}
根据 action 去判断对应行为 给予适应的响应
- 复制操作
override func copy(_ sender: Any?) {
let pastBoard = UIPasteboard.general
pastBoard.string = self.text
}
效果图:

ps: 相信你也注意到了我在第二步中没有给 menuVC 添加 menuItem, 但是为何会有上面的"拷贝"Item? 这是因为我们重写了系统提供的copy(_ :)
函数,所以系统默认给了 menuVC 一个 拷贝
的 menuItem
当我们不使用系统的方法时, 就需要自己给 menuVC 提供一个 menuItem, 自己定义 menuItem 可以给 title
当你自己定义了 menuItem 但是有使用了系统的copy(_ :)
...你会发现比你预计的多一个 menuItem
以下给出了添加 Item 的完整代码
import Foundation
import UIKit
class RNMultiFunctionLabel: UILabel {
override var canBecomeFirstResponder: Bool {
return true
}
override func awakeFromNib() {
super.awakeFromNib()
self.addLongPressGesture()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.addLongPressGesture()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copyText(_:)){
return true
}else{
return false
}
}
override func copy(_ sender: Any?) {
let pastBoard = UIPasteboard.general
pastBoard.string = self.text
}
func copyText(_ sender: Any?){
let pastBoard = UIPasteboard.general
pastBoard.string = self.text
}
}
//MARK: - private methods
extension RNMultiFunctionLabel {
func addLongPressGesture() {
self.isUserInteractionEnabled = true
let longPressGesture = UILongPressGestureRecognizer.init(target: self, action: #selector(longPressAction(gesture:)))
self.addGestureRecognizer(longPressGesture)
}
func longPressAction(gesture: UILongPressGestureRecognizer){
if gesture.state == .began{
self.becomeFirstResponder()
let copyItem = UIMenuItem(title: "复制", action: #selector(copyText(_:)))
let menuVC = UIMenuController.shared
menuVC.menuItems = [copyItem]
if menuVC.isMenuVisible {
return
}
menuVC.setTargetRect(bounds, in: self)
menuVC.setMenuVisible(true, animated: true)
}
}
}
使用
直接继承就行
demo
后续
最近重写了这咯扩展 label, 让点击以及长按手势可以根据你自己的要求实现事件
for example:
contentLabel.pressClosure = { [weak self](gesture) in
if let text = self?.contentLabel.text, text != "" {
if gesture.state == .began{
self?.contentLabel.pressAction(whichLabel: (self?.contentLabel)!)
}
}
}
这是对于长按手势事件的实现, 你可以在闭包内实现你的事件. 我这里self?.contentLabel.pressAction(whichLabel: (self?.contentLabel)!)
是把要实现的操作放在了** RNMultiFunctionLabel**里,一些常用的操作整合在里面,就不用每次都去写
再后续
var isOpenTapGesture: Bool = false { // 是否打开点击手势 -- default: false
didSet{
if isOpenTapGesture {
self.addTapGesture()
}
}
}
var isOpenLongGesture: Bool = true { // 是否打开长按手势 -- default: true
didSet {
self.addLongPressGesture()
}
}
发现之前版本初始化之后打开手势无作用, 所以加上了属性监听.