因為項目中有類似微博的話題和@功能,所以我們來說說類似于新浪微博話題功能的實現(xiàn)颈畸,當文字是”#話題#”這種格式時,該文字字體的顏色得變成藍色。剛拿到這個內(nèi)容猜測的時候應(yīng)該用 UITextView 或 UITextField 去做,了解了思路就去百度了下,發(fā)現(xiàn)真的很少有這樣的案例,得知 YYKIt 大神的 demo 中有這樣的 demo? ?,于是就去下載看看, 這個 YYkit 的鏈接GitHub - ibireme/YYKit: A collection of iOS components.? ??
這個 demo 中有發(fā)布微博界面.
于是想偷懶把整個 copy 過來一下,不用重復(fù)造輪子
當我以為能用的時候我發(fā)現(xiàn),這個刪除是默認不能整體刪除的, YY 大神都是用正則表達式去匹配內(nèi)容,然后進行綁定和變色
于是我就想在這個基礎(chǔ)上看能不能改整體刪除,改了半天還是不行,就在github 上搜索, 找了好久都是標題黨,基本上都是模仿界面,根本沒有實質(zhì)性的內(nèi)容,于是就在谷歌上面搜索,找了好久還是不多
看了一下,發(fā)現(xiàn)新浪微博#話題#和@功能做的并不好,跟上面的情況一樣,仔細發(fā)現(xiàn),新浪微博#話題#和@功能雖然能變聲并不能整體刪除,這個我是測試過的,刪除#再輸入話題會出現(xiàn)正則匹配不正確的現(xiàn)象,@某人刪除文字時候點擊某人會出現(xiàn)查無此人顯現(xiàn), 不知道新浪微博測試和開放人員知不知道,YY 大神的 Demo 也是如如此,今天頭條的發(fā)布是整體刪除,給人感覺很好
于是就谷歌,最后終于找了一篇,
具體實現(xiàn)
在實現(xiàn)過程中并巍,我以AttributedString的顏色值為基準,用幾個正則為查找工具换途,結(jié)合UITextView的三個代理方法懊渡。
/// Prior to replacing text, this method is called to give your delegate a chance to accept or reject the edits. If you do not implement this method, the return value defaults to YES.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
/// The text view calls this method in response to user-initiated changes to the text. This method is not called in response to programmatically initiated changes. Implementation of this method is optional.
- (void)textViewDidChange:(UITextView *)textView;
///? Implementation of this method is optional. You can use the selectedRange property of the text view to get the new selection.
- (void)textViewDidChangeSelection:(UITextView *)textView;
這三個代理方法
shouldChangeTextInRange 代理方法中刽射,第一,實現(xiàn)第一次選中剃执,第二次刪除功能誓禁;第二,實現(xiàn)插入話題后肾档,需要改變其他字符串的初始顏色摹恰,得在這個方法里面做個標志。
textViewDidChange 代理方法中怒见,實現(xiàn) 根據(jù) shouldChangeTextInRange 方法中所得到的標志俗慈,設(shè)置字符串的初始顏色;
textViewDidChangeSelection 代理方法中遣耍,實現(xiàn)讓光標不能移動到話題里面闺阱;
首先我定義了兩個變量,插入了話題以后舵变,繼續(xù)在后面輸入字符的話酣溃,字符顏色就跟話題顏色一樣了。所以纪隙,我得用這兩個變量來實現(xiàn)改變輸入字符的初始顏色赊豌。
/// 改變Range
@property (assign, nonatomic) NSRange changeRange;
/// 是否改變
@property (assign, nonatomic) BOOL isChanged;
哦,對了绵咱,我還得用一個變量來記錄上次光標所在的位置碘饼,因為話題字符串是不讓它輸入的。
/// 光標位置
@property (assign, nonatomic) NSInteger cursorLocation;
用戶從其他界面選擇好話題以后悲伶,它得插入到textview中鞍铡:
NSString *insertText = [NSString stringWithFormat:@"#%@#", dict[KeyTopicName]];
[self.textView insertText:insertText];
NSMutableAttributedString *tmpAString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
[tmpAString setAttributes:@{ NSForegroundColorAttributeName: TopicColor, NSFontAttributeName: DefaultSizeFont } range:NSMakeRange(self.textView.selectedRange.location - insertText.length, insertText.length)];
self.textView.attributedText = tmpAString;
然后我還得找到將用戶所選擇插入的話題位置啊。
/**
*? 得到話題Range數(shù)組
*
*? @return return value description
*/
- (NSArray *)getTopicRangeArray:(NSAttributedString *)attributedString {
? ? NSAttributedString *traveAStr = attributedString ?: _textView.attributedText;
? ? __block NSMutableArray *rangeArray = [NSMutableArray array];
? ? static NSRegularExpression *iExpression;
? ? iExpression = iExpression ?: [NSRegularExpression regularExpressionWithPattern:@"#(.*?)#" options:0 error:NULL];
? ? [iExpression enumerateMatchesInString:traveAStr.string
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? options:0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? range:NSMakeRange(0, traveAStr.string.length)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NSRange resultRange = result.range;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NSDictionary *attributedDict = [traveAStr attributesAtIndex:resultRange.location effectiveRange:&resultRange];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if ([attributedDict[NSForegroundColorAttributeName] isEqual:TopicColor]) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [rangeArray addObject:NSStringFromRange(result.range)];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }];
? ? return rangeArray;
}
那么拢切,三個UITextView delegate方法里的代碼就可以這么玩了:
#pragma mark - UITextView Delegate
- (void)textViewDidChangeSelection:(UITextView *)textView {
? ? NSArray *rangeArray = [self getTopicRangeArray:nil];
? ? BOOL inRange = NO;
? ? for (NSInteger i = 0; i < rangeArray.count; i++) {
? ? ? ? NSRange range = NSRangeFromString(rangeArray[i]);
? ? ? ? if (textView.selectedRange.location > range.location && textView.selectedRange.location < range.location + range.length) {
? ? ? ? ? ? inRange = YES;
? ? ? ? ? ? break;
? ? ? ? }
? ? }
? ? if (inRange) {
? ? ? ? textView.selectedRange = NSMakeRange(self.cursorLocation, textView.selectedRange.length);
? ? ? ? return;
? ? }
? ? self.cursorLocation = textView.selectedRange.location;
}
- (void)textViewDidChange:(UITextView *)textView {
? ? if (_isChanged) {
? ? ? ? NSMutableAttributedString *tmpAString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
? ? ? ? [tmpAString setAttributes:@{ NSForegroundColorAttributeName: [UIColor blackColor], NSFontAttributeName: DefaultSizeFont } range:_changeRange];
? ? ? ? _textView.attributedText = tmpAString;
? ? ? ? _isChanged = NO;
? ? }
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
? ? if ([text isEqualToString:@""]) { // 刪除
? ? ? ? NSArray *rangeArray = [self getTopicRangeArray:nil];
? ? ? ? for (NSInteger i = 0; i < rangeArray.count; i++) {
? ? ? ? ? ? NSRange tmpRange = NSRangeFromString(rangeArray[i]);
? ? ? ? ? ? if ((range.location + range.length) == (tmpRange.location + tmpRange.length)) {
? ? ? ? ? ? ? ? if ([NSStringFromRange(tmpRange) isEqualToString:NSStringFromRange(textView.selectedRange)]) {
? ? ? ? ? ? ? ? ? ? // 第二次點擊刪除按鈕 刪除
? ? ? ? ? ? ? ? ? ? return YES;
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? // 第一次點擊刪除按鈕 選中
? ? ? ? ? ? ? ? ? ? textView.selectedRange = tmpRange;
? ? ? ? ? ? ? ? ? ? return NO;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? } else { // 增加
? ? ? ? NSArray *rangeArray = [self getTopicRangeArray:nil];
? ? ? ? if ([rangeArray count]) {
? ? ? ? ? ? for (NSInteger i = 0; i < rangeArray.count; i++) {
? ? ? ? ? ? ? ? NSRange tmpRange = NSRangeFromString(rangeArray[i]);
? ? ? ? ? ? ? ? if ((range.location + range.length) == (tmpRange.location + tmpRange.length) || !range.location) {
? ? ? ? ? ? ? ? ? ? _changeRange = NSMakeRange(range.location, text.length);
? ? ? ? ? ? ? ? ? ? _isChanged = YES;
? ? ? ? ? ? ? ? ? ? return YES;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? // 話題在第一個刪除后 重置text color
? ? ? ? ? ? if (!range.location) {
? ? ? ? ? ? ? ? _changeRange = NSMakeRange(range.location, text.length);
? ? ? ? ? ? ? ? _isChanged = YES;
? ? ? ? ? ? ? ? return YES;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return YES;
}
好吧,通過以上方法秆吵,基本輸入淮椰、刪除操作功能是實現(xiàn)了
最后是上傳, 上傳是本地先定義一個數(shù)組topicArray
然后在插入數(shù)據(jù)的時候往數(shù)組中插入一個對象
刪除數(shù)據(jù)的時候移除 topicArray 中的對象
最后上傳 textView的 text 內(nèi)容 和數(shù)組中的對象
顯示:
?1:YYLabel 顯示
?2:MLEmojiLabel 顯示
我是用了第二種MLEmojiLabel? ,YYLabel 沒有深入研究,性能比MLEmojiLabel要好,等不忙了再換,
因為我是上傳了文本內(nèi)容和對象給服務(wù)器,服務(wù)器給我類似的對象然后本地坐比對
,到此,爬坑算是結(jié)束了
代碼只是提供思路,沒有做封裝,也沒有深入的去優(yōu)化,希望用到的小伙伴能優(yōu)化并做的更好,謝謝
最后附上 demo?https://github.com/986138497/UITextView-