iOS-你真的会用UIMenuController吗?(详细)

请认真阅读,因为里面有很多细节!

UIMenuController的介绍

  • 什么是UIMenuController?
  • UIMenuController是UIKit里面的控件
  • UIMenuController的作用在开发中弹出的菜单栏
  • 后面介绍了菜单栏显示中文的设置


    菜单栏

有哪些控件是自带UIMenuController菜单栏效果的呢?

  • UITextField
  • UITextView
  • UIWebView
系统菜单栏效果.gif

有些读者问我这个动图是怎么添加的: 我用的是licecap 这个软件
下面送个几个好用的软件

好用的软件

主要介绍两种:
一种是控制器为第一响应者
另一种是当前UI控件为第一响应者 (把方法封装在控件里面)
具体看代码,注释里我会详细讲解,如果实在不懂不妨动手敲一遍

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //首先要允许label可以跟用户交互
    self.label.userInteractionEnabled = YES;
    //给label添加一个敲击手势
    [self.label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelClick)]];
}

/** 点击label触发的方法 */
- (void)labelClick
{
    //控制器不需要调用这个方法, 但是其他乱七八糟的UI控件就需要调用这个方法 \
    因为控制器默认是第一响应者
    //[self becomeFirstResponder];
    
    //显示menu 从来没有让menu跟控制器有关系 , 因为是第一响应者, 所以会调用第一响应者的方法
    
    //不一定调用控制器的方法, 因为现在控制器是第一响应者
    
    
    
    // 获得菜单
    UIMenuController *menu = [UIMenuController sharedMenuController];
    
    // 菜单最终显示的位置
    CGRect rect = CGRectMake(100, 100, 100, 100);
    [menu setTargetRect:rect inView:self.label];
    //为什么要设置2个参数  为了通用 \
    一个是矩形框, 一个是在哪个View上面
    //传了矩形框, 要告诉坐标原点在哪, 坐标原点就在view上\
    以tagreView的左上角为坐标原点
    // 苹果设计2个参数 是因为矩形框一旦修改 出现的位置在哪里都是可以的
    
    /*
     targetRect:menuController指向的矩形框
     targetView:targetRect以targetView的左上角为坐标原点
     */
    
    // 显示菜单
    [menu setMenuVisible:YES animated:YES];
    
    /*
     得通过第一响应者,来告诉MenuController它内部应该显示什么内容
     */
}

#pragma mark - 第一响应者 + UIMenuController
/**
 * 说明控制器可以成为第一响应者
 * 因为控制器是因为比较特殊的对象,它找控制器的方法,不找label的方法
 */
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

/**
 * 通过这个方法告诉UIMenuController它内部应该显示什么内容
 * 返回YES,就代表支持action这个操作
 */
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    //打印, 将一个方法转换成字符串 你就会看到许多方法
    NSLog(@"%@",NSStringFromSelector(action));
    
       if (action == @selector(cut:)
        || action == @selector(copy:)
        || action == @selector(paste:)) {
        return YES;
    }
    
    return NO;
}

//监听事情需要对应的方法 冒号之后传入的是UIMenuController
- (void)cut:(UIMenuController *)menu
{
    NSLog(@"%s %@", __func__, menu);
}

- (void)copy:(UIMenuController *)menu
{
    NSLog(@"%s %@", __func__, menu);
}

- (void)paste:(UIMenuController *)menu
{
    NSLog(@"%s %@", __func__, menu);
}

上面只是简单的介绍了UIMenuController的基本知识,下面正在带你运用到开发中的知识,封装一个自定义控件里面实现, 附上大概效果:

进行工程修改添加支持中文的.png

会跳舞的狮子.gif

上代码,代码有具体的注释,还有具体的用法,所以认真阅读代码即可,就不附上demo了


#import "JHLabel.h"

@implementation JHLabel

/** 不管控件是通过xib stroyboard 还是纯代码  提供两种初始化的操作都调用同一个方法 */
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self setupTap];
    }
    return self;
}
/** 不管控件是通过xib stroyboard 还是纯代码  提供两种初始化的操作都调用同一个方法 */
- (void)awakeFromNib
{
    [self setupTap];
}
/** 设置敲击手势 */
- (void)setupTap
{
    
    self.text = @"author:会跳舞的狮子";
    //已经在stroyboard设置了与用户交互,也可以用纯代码设置
//    self.userInteractionEnabled = YES;
    
    //当前控件是label 所以是给label添加敲击手势
   [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelClick)]];

}
/** 点击label触发的方法 */
- (void)labelClick
{
    // 让label成为第一响应者 \
    一定要写这句话  因为这句话才是主动让label成为第一响应者
    [self becomeFirstResponder];
    
    // 获得菜单
    UIMenuController *menu = [UIMenuController sharedMenuController];
    
    // 设置菜单内容 \
    因为menuItems是数组 官方没有给出需要传入什么对象,但是以经验可以判断出需要传入的是UIMenuItem对象 \
    而且显示是按顺序的
    menu.menuItems = @[
                       [[UIMenuItem alloc] initWithTitle:@"顶" action:@selector(ding:)],
                       [[UIMenuItem alloc] initWithTitle:@"回复" action:@selector(reply:)],
                       [[UIMenuItem alloc] initWithTitle:@"举报" action:@selector(warn:)]
                       ];
    
    // 菜单最终显示的位置 \
    有两种方式: 一种是以自身的bounds  还有一种是以父控件的frame 
    [menu setTargetRect:self.bounds inView:self];
//    [menu setTargetRect:self.frame inView:self.superview];

    // 显示菜单
    [menu setMenuVisible:YES animated:YES];
}

#pragma mark - UIMenuController相关
/**
 * 让Label具备成为第一响应者的资格
 */
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

/**
 * 通过第一响应者的这个方法告诉UIMenuController可以显示什么内容
 */
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if ( (action == @selector(copy:) && self.text) // 需要有文字才能支持复制
        || (action == @selector(cut:) && self.text) // 需要有文字才能支持剪切
        || action == @selector(paste:)
        || action == @selector(ding:)
        || action == @selector(reply:)
        || action == @selector(warn:)) return YES;
    
    return NO;
}

#pragma mark - 监听MenuItem的点击事件
/** 剪切 */
- (void)cut:(UIMenuController *)menu
{
    //UIPasteboard 是可以在应用程序与应用程序之间共享的 \
    (应用程序:你的app就是一个应用程序 比如你的QQ消息可以剪切到百度查找一样)
    // 将label的文字存储到粘贴板
    [UIPasteboard generalPasteboard].string = self.text;
    // 清空文字
    self.text = nil;
}
/** 赋值 */
- (void)copy:(UIMenuController *)menu
{
    // 将label的文字存储到粘贴板
    [UIPasteboard generalPasteboard].string = self.text;
}
/** 粘贴 */
- (void)paste:(UIMenuController *)menu
{
    // 将粘贴板的文字赋值给label
    self.text = [UIPasteboard generalPasteboard].string;
}

//如果方法不实现,是不会显示出来的
- (void)ding:(UIMenuController *)menu
{
    NSLog(@"%s %@", __func__, menu);
}

- (void)reply:(UIMenuController *)menu
{
    NSLog(@"%s %@", __func__, menu);
}

- (void)warn:(UIMenuController *)menu
{
    NSLog(@"%s %@", __func__, menu);
}
@end

上面的代码,menu只显示在label的上面 而一般菜单栏我们都是显示在cell的中间, 下面的这幅图详细讲解了显示在cell的中间

显示在cell内容的中间
/** 点击cell的时候调用 */
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 取出cell
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    
    UIMenuController *menu = [UIMenuController sharedMenuController];
    
    // 设置菜单内容
    menu.menuItems = @[
                       [[UIMenuItem alloc] initWithTitle:@"顶" action:@selector(ding:)],
                       [[UIMenuItem alloc] initWithTitle:@"回复" action:@selector(reply:)],
                       [[UIMenuItem alloc] initWithTitle:@"举报" action:@selector(warn:)]
                       ];
    
    // 显示位置
    CGRect rect = CGRectMake(0, cell.height * 0.5, cell.width, 1);
    [menu setTargetRect:rect inView:cell];
    
    // 显示出来
    [menu setMenuVisible:YES animated:YES];
}

#pragma mark - 获得当前选中的评论
- (JHComment *)selectedComment
{
    // 获得被选中的cell的行号
    NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
    NSInteger row = indexPath.row;
    
    // 获得评论数据
    NSArray *comments = self.shortComments;
    if (indexPath.section == 0 && self.longComments.count) {
        comments = self.longComments;
    }
    
    return comments[row];
}

#pragma mark - UIMenuController处理
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (!self.isFirstResponder) { // 文本框弹出键盘, 文本框才是第一响应者
        if (action == @selector(ding:)
            || action == @selector(reply:)
            || action == @selector(warn:)) return NO;
        //如果是文本框,那么这些显示都不返回
    }
    
    return [super canPerformAction:action withSender:sender];
}

- (void)ding:(UIMenuController *)menu
{
    NSLog(@"ding - %@ %@",
           self.selectedComment.user.username,
           self.selectedComment.content);
}

- (void)reply:(UIMenuController *)menu
{
    NSLog(@"reply - %@ %@",
           self.selectedComment.user.username,
           self.selectedComment.content);
}

- (void)warn:(UIMenuController *)menu
{
    NSLog(@"warn - %@ %@",
           self.selectedComment.user.username,
           self.selectedComment.content);
}

#注: 这里的self.selectedComent
# (点语法  要么调用set方法要么调用get方法, 这里直接用点语法是没有提示的,我已经进行了声明)
#/** 写方法声明的目的是为了使用点语法提示 */
#- (JHComment *)selectedComment;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 回忆呢,就是迟迟的来,久久的痛。当岁月冲刷的只剩定格那一瞬间的记忆,会忽然很怕将来的某个时刻突然在回忆里被扎醒,也...
    雪漫楊阅读 183评论 0 0
  • 斑驳 的 墙 疯狂生长 的 杂草 早已干涸 的 泳池 整齐排放 的 石子 滋啦滋啦 的 篝火
    TheF阅读 108评论 0 0
  • 众所周知,孟加拉豹猫的肠胃非常敏感,当你把心爱的猫咪接回家后,发现猫咪的精神状态非常好,就是一直拉稀或总是时好时坏...
    魅豹豪斯TICA注册猫舍阅读 3,985评论 0 0
  • 最近在学习React,示例代码都由ES6所书写,所以对于ES6,不得不好好研究一下新的语法。这篇文章就对自己现在经...
    2Youngg阅读 363评论 0 1
  • 量变引起质变是我一直信奉的黑格尔主义理论,Quantitative accumulation leads...
    Lady_Lydia阅读 307评论 0 0