前言
UIMenuController
是一种UIKit
中提供的一种使用比较简单的菜单控件。今天无意中看到这个控件,发现对其使用有些模糊了,故此把以前项目中对它的使用抽出来,做一个简单的总结
,旨在指导快速上手。
须知
- 默认情况下只有
UITextField、UITextView、UIWebView
支持并内置UIMenuController
的控件 - 要让其他控件也支持UIMenuController(比如UILabel、UIView等),就必须自定义控件,并实现2个方法特定方法
控件使用效果如下
1. 如何让
UITextField、UITextView、UIWebView
显示UIMenuController
的控件
// 成为第一响应者
[self.label becomeFirstResponder];
UIMenuController *menuController = [UIMenuController sharedMenuController];
// 设置UIMenuItem
menuController.menuItems = menuItems;
// 设置显示方向
menuController.arrowDirection = UIMenuControllerArrowUp;
// targetRect: MenuController需要指向的矩形框
// targetView: targetRect会以targetView的左上角为坐标原点
[menuController setTargetRect:self.label.bounds inView:self.label];
// 显示
[menuController setMenuVisible:YES animated:YES];
2.
自定义
可以使用UIMenuController
的控件(以UILabel为例
)
-
- 自定义一个叫
TLMenuLabel
的Label(这步不是重点,可以直接跳过
)
- 自定义一个叫
typedef void(^TLMenuItemDidClick)(NSString *title,UIMenuController *menu);
@interface TLMenuLabel : UILabel
// 对外API
/** 允许复制:默认YES */
@property (nonatomic,assign) BOOL allowCopy;
/** Ping:默认NO */
@property (nonatomic,assign) BOOL allowPing;
/** tracert:默认NO */
@property (nonatomic,assign) BOOL allowTracert;
/** tcping:默认NO */
@property (nonatomic,assign) BOOL allowTcping;
/** ipconfig:默认NO */
@property (nonatomic,assign) BOOL allowDig;
/** 生成报告:默认NO */
@property (nonatomic,assign) BOOL allowReport;
/** 自定义的MenuItem被点击回调 */
@property (nonatomic,copy) TLMenuItemDidClick itemDidClick;
@end
@implementation TLMenuLabel
// 初始化
- (void)awakeFromNib {
[super awakeFromNib];
[self setup];
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
- (void)setup {
self.userInteractionEnabled = YES;
_allowCopy = YES;
_allowPing = YES;
_allowTracert = NO;
_allowTcping = YES;
_allowDig = NO;
_allowReport = YES;
UILongPressGestureRecognizer *longPress
= [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:@selector(onLongPress:)];
[self addGestureRecognizer:longPress];
}
@end
-
- 重写2个特定方法(
重点
),重写后这个自定义label就支持UIMenuController控件了
- 重写2个特定方法(
// 1. 重写 `- canBecomeFirstResponder`
// ⚠️不能是` - (BOOL)becomeFirstResponder`
- (BOOL)canBecomeFirstResponder{
return self.allowCopy || self.allowPing ||
self.allowTracert || self.allowTcping ||
self.allowDig || _allowReport;
}
// 2. 重写`- canPerformAction: withSender:`
/**
* label能执行哪些操作(比如copy, paste等等)
* @return YES:支持这种操作
*/
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
// 根据你的需求 使能 你需要的操作,这个方法会根据你设置的menuItems多次调用
if (action == @selector(copyText:)){
return self.allowCopy;
}
if (action == @selector(ping:)){
return self.allowPing;
}
if (action == @selector(tracert:)){
return self.allowTracert;
}
if (action == @selector(tcping:)){
return self.allowTcping;
}
if (action == @selector(dig:)){
return self.allowDig;
}
if (action == @selector(report:)){
return self.allowReport;
}
return NO; // 禁止系统操作类型(返回YES,系统操作类型也会显示)
}
-
- 显示(此处通过长按手势唤醒)
- (void)onLongPress:(UILongPressGestureRecognizer *)longPress{
switch (longPress.state) {
case UIGestureRecognizerStateBegan:
{
_bgColor = self.backgroundColor;
self.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
// 需要显示的额外条件
if (self.text == nil || self.text.length < 1) return;
if (![self canBecomeFirstResponder]) return;
[self becomeFirstResponder];
// 创建UIMenuItem
NSArray *titles = @[@"Ping",@"Tracert",@"Tcping",@"Dig",@"生成报告",@"复制"];
NSArray *actions = @[@"ping:",@"tracert:",@"tcping:",@"dig:",@"report:",@"copyText:"];
self.items = [NSMutableArray array];
for (NSString *title in titles) {
NSUInteger index = [titles indexOfObject:title];
// ⚠️要确保这些Action都有实现(见下面第4步)
SEL sel = NSSelectorFromString(actions[index]);
UIMenuItem *newItem = [[UIMenuItem alloc] initWithTitle:title action:sel];
[self.items addObject:newItem];
}
// 获取UIMenuController单例
UIMenuController *controller = [UIMenuController sharedMenuController];
controller.menuItems = self.items;
[controller setTargetRect:self.bounds inView:self];
[controller setMenuVisible:YES animated:YES];
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
self.backgroundColor = _bgColor;
break;
default:
break;
}
}
-
- UIMenuItem对应Action的实现(
此步主要是提醒要实现Action
)
- UIMenuItem对应Action的实现(
- (void)ping:(UIMenuController *)menu
{
if (self.itemDidClick) {
self.itemDidClick(@"ping", menu);
}
}
- (void)tracert:(UIMenuController *)menu
{
if (self.itemDidClick) {
self.itemDidClick(@"tracert -d", menu);
}
}
- (void)tcping:(UIMenuController *)menu
{
if (self.itemDidClick) {
self.itemDidClick(@"tcping", menu);
}
}
- (void)dig:(UIMenuController *)menu
{
if (self.itemDidClick) {
self.itemDidClick(@"dig", menu);
}
}
- (void)report:(UIMenuController *)menu {
if (self.itemDidClick) {
self.itemDidClick(@"report", menu);
}
}
- (void)copyText:(UIMenuController *)menu
{
// 将自己的文字复制到粘贴板
UIPasteboard *board = [UIPasteboard generalPasteboard];
board.string = self.text;
// if (self.itemDidClick) {
// self.itemDidClick(@"copy", menu);
// }
}