iOS 聊天气泡保持选中高亮状态

前言

下图为目前市面上一种常见的聊天会话Cell结构图,如果模糊可以点击放大。
其中主要的自定义媒体区域为CustomContentView,涵盖大部分媒体类型的展示。

会话Cell结构.png

曾经做IM以及朋友圈类似功能的时候,遇到过一个最疑惑的细节问题:

如何实现UIMenuController的指向视图保持选中高亮状态

如下图对比效果:

1. 选中后无状态
2. 选中后保持高亮状态

虽然这只是一个细节,但是我们常说:细节决定成败。

过程

一切问题都是会则不难,难则不会。思考这个问题的过程中,走了不少弯路,曾经想过多种可能解决的方法:

  1. 利用cell的selected状态,在selected方法中做操作。(尝试失败)
  2. 系统自带tableViewcell触发menu高亮效果,来实现这个操作。(尝试失败)
  3. 联系腾讯的相关iOS开发人员问一问。(没去试,谁有门路吗- -)
  4. 其他忘了。

这个问题搁置了许久,虽然暂时没有好的思路,但是也一直没有放弃这个问题。
一次机缘巧合,鼠标点了一下这个东西:

获取targetView

原来是可以拿到这个customContentView的!
不过UIMenuController里没有直接提供接口,如图:

系统提供的API

但是iOS开发者都知道,经常需要用到KVC来取值。没错,我们能看到有这个targetView就可以尝试用 valueForKey:来获取这个视图。
注意,这种获取_targetView的方法在iOS11之前会导致崩溃,所以可以在控制器触发onLongPressCell:里用一个全局变量记录这个View。
思路理清以后,事实证明这种方法是可行的,终于解决了这个困扰许久的问题。

实现

1. 对cell添加长按事件,可以用delegate或者block返回事件传给ViewController。
- (void)longGesturePress:(UIGestureRecognizer*)gestureRecognizer {
    if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
        gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        if (_messageDelegate && [_messageDelegate respondsToSelector:@selector(onLongPressCell:inView:)]) {
            // 实现来自ViewController的messageDelegate 并将 CustomContentView传递
            [_messageDelegate onLongPressCell:self.model.message inView:_customContentView];  
        }
    }
}

接下来,ViewController立刻来响应这个代理方法。 侍从,来我身边!


- (void)onLongPressCell:(NIMMessage *)message inView:(UIView *)view {
    NSArray *items = [self menusItems:message];
    if ([items count] && [self becomeFirstResponder]) {
        UIMenuController *controller = [UIMenuController sharedMenuController];
        controller.menuItems = items;
        self.messageForMenu = message;
        self.menuTargetView = view;  // 记录该被点击的视图
        [controller setTargetRect:view.bounds inView:view]; // 这一步很关键
        [controller setMenuVisible:YES animated:YES];
    }
}

2. ViewController添加监听UIMenuController的通知。
// 这5种NSNotificationName都是UIMenuController的系统自带监听方案
UIKIT_EXTERN NSNotificationName const UIMenuControllerWillShowMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerDidShowMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerWillHideMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerDidHideMenuNotification __TVOS_PROHIBITED;
UIKIT_EXTERN NSNotificationName const UIMenuControllerMenuFrameDidChangeNotification __TVOS_PROHIBITED;

我们根据需要添加一种Show以及一种Hide即可。Show:设置高亮,Hide:恢复正常。

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(menuWillShow:)
                                                 name:UIMenuControllerWillShowMenuNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(menuDidHide:)
                                                 name:UIMenuControllerDidHideMenuNotification
                                               object:nil];
3. 在通知触发方法中,对targetView进行变更高亮/正常处理操作。
// 菜单将要出现
- (void)menuWillShow:(NSNotification *)notification {
    [self showMenuControllerChangeTargetView:YES notification:notification];
}

// 菜单已经隐藏
- (void)menuDidHide:(NSNotification *)notification {
    [UIMenuController sharedMenuController].menuItems = nil;
    [self showMenuControllerChangeTargetView:NO notification:notification];
}

// 设置图片方法
- (void)showMenuControllerChangeTargetView:(BOOL)isHighlight notification:(NSNotification *)noti {
    UIControlState state = isHighlight ? UIControlStateHighlighted : UIControlStateNormal;
    //  id base = [noti.object valueForKey:@"_targetView"]; 
    // 拿到MenuController指向的targetView
    id base = self.menuTargetView;
    if ([base isKindOfClass:[IM_Session_ContentView_BaseView class]]) {
        NIMMessageModel * model = [base model];
        // 此方法是根据1.消息来源 2.选中状态 来拿到bubbleImage(resizable)
        UIImage *image = [base chatBubbleImageForState:state outgoing:model.message.isOutgoingMsg];  
        [[base bubbleImageView] setImage:image];  // 设置图片样式
    }
}

我把IM_Session_ContentView_BaseView里这个方法拿出来供参考。BaseView是个基类视图,其他类型的contentView继承该父类即可避免多处写入该方法。

- (UIImage *)chatBubbleImageForState:(UIControlState)state outgoing:(BOOL)outgoing{
    NSString * imageName;
    if (outgoing) {  // 发出的消息
        if (state == UIControlStateNormal) {
            imageName = @"im_msgbg_white_right";
        } else if (state == UIControlStateHighlighted)  {
            imageName = @"im_msgbg_white_right_click";
        }
    } else {  // 收到的消息
        if (state == UIControlStateNormal) {
            imageName = @"im_msgbg_white_left";
        } else if (state == UIControlStateHighlighted) {
            imageName = @"im_msgbg_white_left_click";
        }
    }
    if (imageName) {
        UIImage *image = [UIImage imageNamed:imageName];
        return [image resizableImageWithCapInsets:UIEdgeInsetsMake(18,25,17,25)
                                     resizingMode:UIImageResizingModeStretch];
    }
    return nil;
}

最后

如有疑问或者有其他iOS问题可以留言,一起学习进步。
也可以联系我,邮箱xingjl@outlook.com、 qq12087014。

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