iOS中@功能的完整实现

点击上方“iOS开发”,选择“置顶公众号”

关键时刻,第一时间送达!

640?

640?wx_fmt=gif


哼哼想不到吧,我又回来啦!好久没写文章了,以后尽量多写写吧。最近看到有人问@功能的需求,就大概写了写,先看看实现效果:


640?wx_fmt=gif

效果图


这个功能的具体要求如下:


1、一个@是由一个@字符和一个空格字符包起来的。
2、支持手写输入,只要符合就高亮显示。
3、支持从列表选择,选择后插入光标所在位置并高亮。
4、光标不能出现在一个@词中间,点击中间后自动移动到@词后面,长按滑动光标时也要越过@词。但是当用户长按选择文本时可以。
5、当光标正好在一个@词后面时,按删除键@词要整体删除。

先附上本文demo(https://github.com/lisongrc/ATDemo),算下来代码也没有多少,还算简洁,大家一看就懂。其中包括的功能:


1、输入框@编辑和选择功能,也就是上面那些需求。
2、输入框随着文字多少改变高度,并根据键盘随动。
3、发布后显示在列表上,并将符合的@高亮显示。
4、列表上的cell根据文字自动计算高度。
5、点击高亮词后可以捕获到事件,自己实现跳转就可以。

好了,下面大概讲解一下,有不明白的地方可以下载demo具体看看。

用到的第三方


1、HPGrowingTextView(https://github.com/HansPinckaers/GrowingTextView),用来实现输入框根据文字改变高度。使用和UITextView类似,代理也和UITextView差不多:


640?wx_fmt=pngHPGrowingTextViewDelegate


2、MLLabel,用来高亮显示label文本中的某些文字,支持自动识别一些常见的,也可以自定义规则。支持链接色和点击色等等一些配置。点击回调里面自己实现跳转就可以。


640?wx_fmt=png

MLLabel


集成这些第三方用的是cocoapods,关于cocoapods的教程可以看我的这篇文章;


具体实现的一些细节


1、检验文本中所有的@词


用的是系统的NSRegularExpression类,不熟悉的大家可以去查一下,这里就不细讲了:


- (NSArray<NSTextCheckingResult *> *)findAllAt
{
   // 找到文本中所有的@
   NSString *string = self.growingTextView.text;
   NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:kATRegular options:NSRegularExpressionCaseInsensitive error:nil];
   NSArray *matches = [regex matchesInString:string options:NSMatchingReportProgress range:NSMakeRange(0, [string length])];
   return matches;
}


2、输入框根据输入文字多少自动改变高度


在HPGrowingTextView的代理里面实现:


- (void)growingTextView:(HPGrowingTextView *)growingTextView willChangeHeight:(float)height
{    
   self.commentViewHeight.constant = height + 14;
}


3、输入框中的文字要随着文字改变实时将@词高亮


- (void)growingTextViewDidChange:(HPGrowingTextView *)growingTextView
{
   UITextRange *selectedRange = growingTextView.internalTextView.markedTextRange;
   NSString *newText = [growingTextView.internalTextView textInRange:selectedRange];
   if (newText.length < 1)
   {
       // 高亮输入框中的@
       UITextView *textView = self.growingTextView.internalTextView;
       NSRange range = textView.selectedRange;
       NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:textView.text];
       [string addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, string.string.length)];
       NSArray *matches = [self findAllAt];
       for (NSTextCheckingResult *match in matches)
       {
           [string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(match.range.location, match.range.length - 1)];
       }
       textView.attributedText = string;
       textView.selectedRange = range;
   }
}


首先判断是不是正在选择文本,不是的话我们才应该处理。然后就是用正则找到所有的@词,用NSMutableAttributedString的方法加上高亮色,然后把最终的attributedString赋值给TextView,并将光标的位置复原为替换文本之前的状态。


4、删除时@词要整体删除


- (BOOL)growingTextView:(HPGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
   if ([text isEqualToString:@""])
   {
       NSRange selectRange = growingTextView.selectedRange;
       if (selectRange.length > 0)
       {
           //用户长按选择文本时不处理
           return YES;
       }
       // 判断删除的是一个@中间的字符就整体删除
       NSMutableString *string = [NSMutableString stringWithString:growingTextView.text];
       NSArray *matches = [self findAllAt];
       BOOL inAt = NO;
       NSInteger index = range.location;
       for (NSTextCheckingResult *match in matches)
       {
           NSRange newRange = NSMakeRange(match.range.location + 1, match.range.length - 1);
           if (NSLocationInRange(range.location, newRange))
           {
               inAt = YES;
               index = match.range.location;
               [string replaceCharactersInRange:match.range withString:@""];
               break;
           }
       }
       if (inAt)
       {
           growingTextView.text = string;
           growingTextView.selectedRange = NSMakeRange(index, 0);
           return NO;
       }
   }
   //判断是回车键就发送出去
   if ([text isEqualToString:@" "])
   {
       [self.comments addObject:growingTextView.text];
       self.growingTextView.text = @"";
       [self.growingTextView resignFirstResponder];
       [self.tableView reloadData];
       return NO;
   }
   return YES;
}


首先判断替换词是空字符串就代表是删除操作,然后找出输入框文字中所有的@词,判断要删除的字符是否在任意一个@词中间,如果在就把输入框文字中这个@词整体删除,然后重新赋值给TextView,并纠正光标的位置。但是要判断用户在长按选择文本时不处理。


5、光标不能点击落在一个@词中间:


- (void)growingTextViewDidChangeSelection:(HPGrowingTextView *)growingTextView
{
   // 光标不能点落在@词中间
   NSRange range = growingTextView.selectedRange;
   if (range.length > 0)
   {
       // 选择文本时可以
       return;
   }
   NSArray *matches = [self findAllAt];
   for (NSTextCheckingResult *match in matches)
   {
       NSRange newRange = NSMakeRange(match.range.location + 1, match.range.length - 1);
       if (NSLocationInRange(range.location, newRange))
       {
           growingTextView.internalTextView.selectedRange = NSMakeRange(match.range.location + match.range.length, 0);
           break;
       }
   }
}


其实就是判断光标改变位置后是否在@词中间,如果在就把光标强制移动到@词后面。但是当用户长按选择文本时可以。


6、从列表中选择人去@他


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
   // 去选择@的人
   [self.growingTextView.internalTextView unmarkText];
   NSInteger index = self.growingTextView.text.length;
   if (self.growingTextView.isFirstResponder)
   {
       index = self.growingTextView.selectedRange.location + self.growingTextView.selectedRange.length;
       [self.growingTextView resignFirstResponder];
   }
   SelectUserController *atVC = segue.destinationViewController;
   atVC.selectBlock = ^(NSString *name)
   {
       UITextView *textView = self.growingTextView.internalTextView;
       NSString *insertString = [NSString stringWithFormat:kATFormat,name];
       NSMutableString *string = [NSMutableString stringWithString:textView.text];
       [string insertString:insertString atIndex:index];
       self.growingTextView.text = string;
       [self.growingTextView becomeFirstResponder];
       textView.selectedRange = NSMakeRange(index + insertString.length, 0);
   };
}


其实就是选择后将@词插入到光标位置,并将光标纠正到@词后面。


7、评论列表cell上的@高亮并可点击。使用MLLabel实现起来还是比较简单的:


- (void)setComment:(NSString *)comment
{
   _comment = comment;
   self.titleLabel.text = comment;
   // 高亮@
   NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:kATRegular options:NSRegularExpressionCaseInsensitive error:nil];
   [regex enumerateMatchesInString:comment options:NSMatchingReportProgress range:NSMakeRange(0, comment.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop)
   {
       [self.titleLabel addLinkWithType:MLLinkTypeUserHandle value:comment range:result.range];
   }];
}


self
.titleLabel.didClickLinkBlock = ^(MLLink *link, NSString *linkText, MLLinkLabel *label)
{
   NSLog(@"点击了%@",linkText);
};



8、cell高度自动计算,系统自己支持,不了解的可以看我的这篇文章(https://www.jianshu.com/p/64f0e1557562)


好了,大概就是这么些东西,是不是很简单呢,如果还有不明白的地方就下载本文demo仔细看看吧。

(https://github.com/lisongrc/ATDemo)


640?

  • 作者:iOS_小松哥

  • 链接:https://www.jianshu.com/p/eabf5a944158

  • iOS开发整理发布,转载请联系作者获得授权

640?wx_fmt=gif

640?【点击成为源码大神】

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值