美文网首页
iOS 聊天气泡保持选中高亮状态

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

作者: Rex_ | 来源:发表于2018-05-23 14:52 被阅读0次

前言

下图为目前市面上一种常见的聊天会话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。

相关文章

网友评论

      本文标题:iOS 聊天气泡保持选中高亮状态

      本文链接:https://www.haomeiwen.com/subject/mdwhjftx.html