1. 程式人生 > >iOS中@功能的完整實現

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