在很多应用当中, 当我们长按一段文字或者图片的时候会弹出一个菜单,我们通过这个菜单可以实现文字的复制、剪切、删除和各种操作:这个菜单就是UIMenuController,系统默认支持UITextField、UITextView、UIWebView控件的UIMenuController操作;对于系统不支持的UIMenuController操作的控件,我们就要自定义控件的UIMenuController来实现相关功能。
一. UIMenuController相关方法
创建一个UIMenuController对象
#if UIKIT_DEFINE_AS_PROPERTIES
@property(class, nonatomic, readonly) UIMenuController *sharedMenuController;
#else
+ (UIMenuController *)sharedMenuController;
#endif
class语义属于类属性, 与swift的类属性相对应
显示或隐藏菜单
/**
* 设置menu显示的位置信息
*
* @param targetRect menu需要显示的矩形区域
* @param targetView targetRect会以targetView的左上角为坐标原点进行显示
*/
- (void)setTargetRect:(CGRect)targetRect inView:(UIView *)targetView;
// 菜单相对于矩形的位置
@property(nonatomic) UIMenuControllerArrowDirection arrowDirection NS_AVAILABLE_IOS(3_2); // default is UIMenuControllerArrowDefault
注意
1. targetRect一旦设定以后,矩形范围不会跟随view的移动而移动,如果view移动,必须相应的更新targetRect 。比如tableView 点击cell出现menu,当按住对应的cell,拖动tableView滚动时,menu不会随着对应的cell一起滚动
2. targetRect通常设置为需要弹出menu控件的bounds,targetView设置为对应的控件本身
更新menu的显示与对应的方法
- (void)update;
自定义menuItem
@property(nullable, nonatomic,copy) NSArray<UIMenuItem *> *menuItems NS_AVAILABLE_IOS(3_2); // default is nil. these are in addition to the standard items
NS_CLASS_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED @interface UIMenuItem : NSObject
- (instancetype)initWithTitle:(NSString *)title action:(SEL)action NS_DESIGNATED_INITIALIZER;
@property(nonatomic,copy) NSString *title;
@property(nonatomic) SEL action;
@end
编辑菜单箭头指向view的位置
默认取决于view在界面的位置
typedef NS_ENUM(NSInteger, UIMenuControllerArrowDirection) {
UIMenuControllerArrowDefault, // up or down based on screen location
UIMenuControllerArrowUp NS_ENUM_AVAILABLE_IOS(3_2),
UIMenuControllerArrowDown NS_ENUM_AVAILABLE_IOS(3_2),
UIMenuControllerArrowLeft NS_ENUM_AVAILABLE_IOS(3_2),
UIMenuControllerArrowRight NS_ENUM_AVAILABLE_IOS(3_2),
}
menu支持的通知
UIMenuControllerWillShowMenuNotification;
UIMenuControllerDidShowMenuNotification;
UIMenuControllerWillHideMenuNotification;
UIMenuControllerDidHideMenuNotification;
UIMenuControllerMenuFrameDidChangeNotification;
二. 自定义控件的UIMenuController
两个重要方法(如果自定义的menu显示效果存在问题,一般是这两个方法出现了问题)
//设置控件可以成为第一响应者,注意不是每个控件都可以成为第一响应者
- (BOOL)canBecomeFirstResponder; // default is NO
/**
* 设置控件能够执行那些具体操作
* @param action 具体操作
* @return YES:支持该操作
*/
- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender;
// Allows an action to be forwarded to another target. By default checks -canPerformAction:withSender: to either return self, or go up the responder chain.
一般步骤
1. 设置控件成为第一响应者 2. 创建UIMenuController 3. 创建UIMenuItem(如果需要自定义item) 4. 在对应控件上重写上述两个方法
UIMenuController按钮点击常见系统方法
- (void)cut:(nullable id)sender NS_AVAILABLE_IOS(3_0);
- (void)copy:(nullable id)sender NS_AVAILABLE_IOS(3_0);
- (void)paste:(nullable id)sender NS_AVAILABLE_IOS(3_0);
- (void)select:(nullable id)sender NS_AVAILABLE_IOS(3_0);
- (void)selectAll:(nullable id)sender NS_AVAILABLE_IOS(3_0);
- (void)delete:(nullable id)sender NS_AVAILABLE_IOS(3_2);
- (void)makeTextWritingDirectionLeftToRight:(nullable id)sender NS_AVAILABLE_IOS(5_0);
- (void)makeTextWritingDirectionRightToLeft:(nullable id)sender NS_AVAILABLE_IOS(5_0);
//私有方法
_promptForReplace:
_transliterateChinese:
_showTextStyleOptions:
_define:
_addShortcut:
_accessibilitySpeak:
_accessibilitySpeakLanguageSelection:
_accessibilityPauseSpeaking:
_share:
自定义label的UIMenuController
#import "CopyLabel.h"
@implementation CopyLabel
// 当nib文件被加载的时候
- (void)awakeFromNib {
[super awakeFromNib];
}
// 当nib文件对象被实例化的时候
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self setUpViews];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setUpViews];
}
return self;
}
- (void)setUpViews {
self.userInteractionEnabled = YES;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressAction:)];
[self addGestureRecognizer:longPress];
}
- (void)longPressAction:(UILongPressGestureRecognizer *)sender {
// 1. 设置label为第一响应者
[self becomeFirstResponder];
// 2. 设置UIMenuController
UIMenuController *menu = [UIMenuController sharedMenuController];
// 当长按label的时候,这个方法会被不断调用, menu就会出现一闪一闪不断显示,需要在此处进行判断
if (menu.isMenuVisible) {
return;
}
// 自定义UIMenuController
UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:@"剪切" action:@selector(myCut:)];
UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:@"粘贴" action:@selector(myPaste:)];
menu.menuItems = @[item1, item2];
[menu setTargetRect:self.bounds inView:self];
[menu setMenuVisible:YES animated:YES];
}
#pragma mark - 对控件权限进行设置
/**
设置label可以成为第一响应者
注意:不是每个控件都有资格成为第一响应者
*/
- (BOOL)canBecomeFirstResponder {
return YES;
}
/**
label能够执行哪些具体操作
@param action 具体操作
@return YES:支持该操作
*/
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == @selector(myCut:) || action == @selector(myPaste:)) {
return YES;
}
return NO;
}
- (void)myCut:(UIMenuController *)menu {
[self copy:menu];
self.text = nil;
}
- (void)myPaste:(UIMenuController *)menu {
UIPasteboard *paste = [UIPasteboard generalPasteboard];
if (paste.string) {
self.text = paste.string;
}
}
- (void)copy:(UIMenuController *)menu {
// 当没有文字的时候调用这个方法会崩溃
if (!self.text) {
return;
}
// 复制文字到剪切板
UIPasteboard *paste = [UIPasteboard generalPasteboard];
paste.string = self.text;
}
@end
自定义button的UIMenutroller
#import "SharedButton.h"
@implementation SharedButton
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)buttonAction:(UIButton *)sender {
[self becomeFirstResponder];
UIMenuController *menu = [UIMenuController sharedMenuController];
UIMenuItem *item0 = [[UIMenuItem alloc] initWithTitle:@"分享" action:@selector(sharedAction:)];
UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:@"评论" action:@selector(commentsAction:)];
UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:@"点赞" action:@selector(praiseAction:)];
menu.menuItems = @[item0, item1, item2];
menu.arrowDirection = UIMenuControllerArrowRight;
[menu setTargetRect:self.bounds inView:self];
[menu setMenuVisible:YES animated:YES];
}
- (void)sharedAction:(UIMenuController *)menu {
NSLog(@"分享");
}
- (void)commentsAction:(UIMenuController *)menu {
NSLog(@"评论");
}
- (void)praiseAction:(UIMenuController *)menu {
NSLog(@"点赞");
}
UITableViewCell的UIMenuController
1.在TableViewCell里实现方法:
- (BOOL)canBecomeFirstResponder {
return YES;
}
2.在controller里实现方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIMenuController *menu = [UIMenuController sharedMenuController];
if (menu.isMenuVisible) {
[menu setMenuVisible:NO animated:YES];
} else {
TableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
self.selectedCell = cell;
[cell becomeFirstResponder];
UIMenuItem *item0 = [[UIMenuItem alloc] initWithTitle:@"分享" action:@selector(shareAction:)];
UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:@"评论" action:@selector(commentsAction:)];
UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:@"点赞" action:@selector(praiseAction:)];
menu.menuItems = @[item0, item1, item2];
[menu setTargetRect:CGRectMake(0, cell.frame.size.height * 0.5, cell.frame.size.width, cell.frame.size.height) inView:cell.contentView];
[menu setMenuVisible:YES animated:YES];
}
}
- (void)shareAction:(UIMenuController *)menu {
NSLog(@"分享%@", self.selectedCell.nameLabel.text);
}
- (void)commentsAction:(UIMenuController *)menu {
NSLog(@"评论%@", self.selectedCell.nameLabel.text);
}
- (void)praiseAction:(UIMenuController *)menu {
NSLog(@"点赞%@", self.selectedCell.nameLabel.text);
}