融云聊天页面长按消息后“翻译”功能的实现方法

124 阅读3分钟

项目要求实现“翻译”的功能,融云 SDK 本身没这个功能,所以只能曲线救国了,通过自定义消息来实现,下面是功能实现相关内容。

资源链接:

官网:www.rongcloud.cn/ 自定义消息文档:docs.rongcloud.cn/v4/views/im…

实现思路

  1. 创建自定义 cell,与 SDK 内置的文本消息进行绑定。因为他们内置的文本消息 cell 不支持扩展显示翻译的内容,所以需要使用自定义 cell。
  2. 在聊天页面将自定义 cell 与内置的文本消息进行绑定。
  3. 重写长按消息 cell 的方法,判断如果是文本消息,增加“翻译”按钮。
  4. 点击“翻译”按钮,对文本内容进行翻译,并将翻译好的内容设置到数据源中。
  5. 刷新 UI,会触发自定义 cell 中的回调方法,在回调方法中重新设置高度,并添加 UI,对数据源中翻译好的内容进行展示。

代码部分

  1. 创建自定义 cell 继承于 RCTextMessageCell,m 文件中代码如下,具体效果可自行调整

    #import "RCDTextMessageCell.h"
    
    #define RCDScreenWidth [UIScreen mainScreen].bounds.size.width
    #define Font_Size 16
    #define Extra_BackgroupView_CornerRadius 6.f
    
    @interface RCDTextMessageCell ()
    
    //翻译内容的 Label
    @property (strong, nonatomic) UILabel *extraLabel;
    
    //翻译内容的背景图
    @property (strong, nonatomic) UIView *extraBackgroundView;
    
    @end
    
    @implementation RCDTextMessageCell
    
    + (CGSize)sizeForMessageModel:(RCMessageModel *)model
          withCollectionViewWidth:(CGFloat)collectionViewWidth
             referenceExtraHeight:(CGFloat)extraHeight {
        //翻译好的内容
        NSString *extra = model.extra;
        
        CGSize superSize = [super sizeForMessageModel:model withCollectionViewWidth:collectionViewWidth referenceExtraHeight:extraHeight];
        if (extra.length > 0) {
            CGSize extraSize = [RCDTextMessageCell getTextLabelSize:extra];
            CGFloat finalHeight = superSize.height + extraSize.height;
            return CGSizeMake(superSize.width, finalHeight);
        }else {
            return superSize;
        }
    }
    
    //计算文本 size 的方法
    + (CGSize)getTextLabelSize:(NSString *)content {
        if ([content length] > 0) {
            float maxWidth = RCDScreenWidth -
            (10 + [RCIM sharedRCIM].globalMessagePortraitSize.width + 10) * 2 - 5 - 35;
            CGRect textRect = [content
                               boundingRectWithSize:CGSizeMake(maxWidth, 8000)
                               options:(NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin |
                                        NSStringDrawingUsesFontLeading)
                               attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:Font_Size]}
                               context:nil];
            textRect.size.height = ceilf(textRect.size.height);
            textRect.size.width = ceilf(textRect.size.width);
            return CGSizeMake(textRect.size.width + 5, textRect.size.height + 5);
        } else {
            return CGSizeZero;
        }
    }
    
    - (void)setDataModel:(RCMessageModel *)model {
        [super setDataModel:model];
        NSString *extra = model.extra;
        
        //为了防止复用问题的处理
        [self.extraLabel removeFromSuperview];
        [self.extraBackgroundView removeFromSuperview];
        
        //如果有翻译的内容,添加 UI,进行展示
        if (extra.length > 0) {
            CGSize extraSize = [[self class] getTextLabelSize:extra];
            CGRect superFrame = self.bubbleBackgroundView.frame;
            CGRect extraBackgroundViewFrame = CGRectZero;
            
            //判断消息方向,设置翻译背景图的 frame
            if (model.messageDirection == MessageDirection_SEND) {
                extraBackgroundViewFrame = CGRectMake(superFrame.origin.x + superFrame.size.width - extraSize.width - 15, superFrame.size.height + 3, extraSize.width + 10, extraSize.height + 6);
            } else {
                extraBackgroundViewFrame = CGRectMake(superFrame.origin.x + 5, superFrame.origin.y + superFrame.size.height + 3, extraSize.width + 10, extraSize.height + 6);
            }
            
            self.extraBackgroundView = [[UIView alloc] initWithFrame:extraBackgroundViewFrame];
            self.extraBackgroundView.backgroundColor = [UIColor whiteColor];
            self.extraBackgroundView.layer.cornerRadius = Extra_BackgroupView_CornerRadius;
            [self.messageContentView addSubview:self.extraBackgroundView];
            
            CGRect extraLabelFrame = CGRectMake(8, 3, extraSize.width, extraSize.height);
            self.extraLabel = [[UILabel alloc] initWithFrame:extraLabelFrame];
            self.extraLabel.font = [UIFont systemFontOfSize:Font_Size];
            self.extraLabel.numberOfLines = 0;
            [self.extraLabel setLineBreakMode:NSLineBreakByWordWrapping];
            [self.extraLabel setTextAlignment:NSTextAlignmentLeft];
            [self.extraBackgroundView addSubview:self.extraLabel];
            self.extraLabel.text = extra;
        }
    }
    
    @end
    
  2. 在聊天页面类导入并绑定自定义 cell :

    #import "RCDTextMessageCell.h"
    
    - (void)viewDidLoad {
        [super viewDidLoad];    
        [self registerClass:[RCDTextMessageCell class] forMessageClass:[RCTextMessage class]];
    }
    
  3. 在聊天页面实现长按消息的“翻译”方法:

    • 创建一个属性用于暂存长按时候的 model。
    @property (strong, nonatomic) RCMessageModel *longTouchModel;
    
    • 重写长按消息 cell 的方法,判断如果是文本消息,增加“翻译”按钮,translate 是实现“翻译”的方法。

      - (NSArray<UIMenuItem *> *)getLongTouchMessageCellMenuList:(RCMessageModel *)model {
          NSMutableArray<UIMenuItem *> *menuList = [[super getLongTouchMessageCellMenuList:model] mutableCopy];
          if ([model.content isKindOfClass:[RCTextMessage class]]) {
              UIMenuItem *forwardItem = [[UIMenuItem alloc] initWithTitle:@"翻译"
                                                                   action:@selector(translate)];
              self.longTouchModel = model;
              [menuList addObject:forwardItem];
          }
          return menuList;
      }
      
  4. 实现“翻译”方法,并将翻译好的内容设置到数据源中:

    • 代码仅供参考,翻译的方法还需要自己来实现,把最终翻译好的结果赋值给 self.longTouchModel.extra
    • 这里必须要提的是 cellSize = CGSizeZero,否则刷新 UI 不会改变 cell 的高度
    • 刷新 self.conversationMessageCollectionView
    • 如果翻译的是最后一条消息,需要滚动到底部,否则翻译的内容会被遮挡
    - (void)translate {
        if (self.longTouchModel) {
            NSString *result = @"翻译后的结果。";
            RCTextMessage *txtMsg = (RCTextMessage *)self.longTouchModel.content;
            if ([txtMsg.content isEqualToString:@"How are you?"]) {
                result = @"你好吗?";
            } else if ([txtMsg.content isEqualToString:@"I’m fine."]) {
                result = @"我很好。";
            }
            self.longTouchModel.extra = result;
            self.longTouchModel.cellSize = CGSizeZero;
            [self.conversationMessageCollectionView reloadData];
            RCMessageModel *model = [self.conversationDataRepository lastObject];
            if (model.messageId == self.longTouchModel.messageId ) {
                [self scrollToBottomAnimated:YES];
            }
        }
    }
    
  5. 刷新 UI 时,显示翻译的内容,自定义 cell 会回调设置 CGSize 的方法,改变 cell 高度,同时也会回调 setDataModel 方法,在这个方法中添加翻译的内容,具体可以参考自定义 cell 的 m 文件中代码,这里就不重复贴代码了。

到此为止,该功能已经算是搞定了,同样这个思路和处理方法也适用于“语音转文字”,把翻译功能换成语音转文字,cell 继承于 RCVoiceMessageCell 就完了,希望这篇分享能帮助到大家。