在使用UITextView控件时长按会弹出拷贝、全选、剪切、粘贴等菜单。但页面刚加载时怎么自动显示UIMenuController的菜单呢?(领导的奇葩需求,可能有点懒不想长按。(⊙o⊙)…)
1、声明全局变量contentTV、menuController,布局UITextView控件和显示文本内容。初始化UIMenuController,添加自定义菜单选项。
UIMenuItem *note = [[UIMenuItem alloc] initWithTitle:@"自定义" action:@selector(customMenuItemClick:)];
self.menuController = [UIMenuController sharedMenuController];
[self.menuController setMenuItems:[NSArray arrayWithObject:note]];
2、必须实现的方法:
(a)UITextView变为第一响应和设置选中的文本起始位置
[self.contentTV becomeFirstResponder];
self.contentTV.selectedRange = NSMakeRange(0, 2);
(b)设置是否显示菜单
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == @selector(customMenuItemClick:)) {
if (self.contentTV.selectedRange.length > 0) {
return YES;
}
}
return NO;
}
3、添加UIMenuController通知、实现方法,通过计算x轴改变箭头的位置,通过计算y轴改变菜单显示的位置。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(MenuFrameDidChangeNoti:) name:UIMenuControllerMenuFrameDidChangeNotification object:nil];
- (void)MenuFrameDidChangeNoti:(NSNotification *)notifi {
//不在主线程会崩溃
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat offsetX = -(kSCREEN_WIDTH/2-47); //改变箭头位置
CGFloat offsetY = self.contentTV.textContainerInset.top;
if (self.contentTV.contentSize.height > kSCREEN_HEIGHT) {
offsetY = -10;
}
if (@available(iOS 13.0, *)) {
[self.menuController showMenuFromView:self.contentTV rect:CGRectMake(offsetX, offsetY, self.contentTV.bounds.size.width, 45)];
}else {
[self.menuController setTargetRect:CGRectMake(offsetX, offsetY, self.contentTV.bounds.size.width, 45) inView:self.contentTV];
}
});
}
4、在viewDidAppear里面 menuVisible属性设为YES会触发通知
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.menuController.menuVisible = YES;
}
注意:如果在其他操作中重新设置menuVisible属性,可能会造成通知循环。
5、如果UITextView在滑动时想要使UIMenuController不显示,让UITextView失去第一响应,并调用hideMenu或setMenuVisible: animated:方法。
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
//滑动时要想不显示菜单,先使contentTV失去第一响应
if ([self.contentTV isFirstResponder]) {
[self.contentTV resignFirstResponder];
}
if (@available(iOS 13.0, *)) {
[self.menuController hideMenuFromView:contentTV];
}else {
[self.menuController setMenuVisible:NO animated:YES];
}
}
6、另一种显示菜单的方式,不使用通知,直接在viewDidAppear显示
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CGFloat offsetX = -(SCREEN_WIDTH/2-47); //改变箭头位置
CGFloat offsetY = contentTV.textContainerInset.top;
if (contentTV.contentSize.height > SCREEN_HEIGHT) {
offsetY = -10;
}
if (@available(iOS 13.0, *)) {
[self.menuController showMenuFromView:contentTV rect:CGRectMake(offsetX, offsetY, contentTV.bounds.size.width, 45)];
}else {
self.menuController.menuVisible = YES;
[self.menuController setTargetRect:CGRectMake(offsetX, offsetY, contentTV.bounds.size.width, 45) inView:contentTV];
}
}
7、需要注意的是menuVisible为YES时,在13.0以下的版本会造成页面消失后菜单不会消失和拖动光标选择文字时菜单不会变化。解决这两个问题:在合适的位置把menuVisible属性置为NO。
- (void)textViewDidChangeSelection:(UITextView *)textView {
if (self.menuController.menuVisible) {
if (@available(iOS 13.0, *)) {
[self.menuController hideMenuFromView:self.contentTV];
}else {
self.menuController.menuVisible = NO;
}
}
}
Demo地址:https://github.com/hellodream886/TYMenuController.git