// // MentionTextView.m // AIIM // // Created by qitewei on 2025/5/30. // #import "MentionTextView.h" @implementation MentionTextView{ NSMutableArray *_mentionRanges; } - (NSArray *)mentionRanges { return [_mentionRanges copy]; } - (void)setMentionRanges:(NSArray *)mentionRanges { _mentionRanges = [mentionRanges mutableCopy]; } - (void)setup { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self]; [self detectMentions]; } - (void)textDidChange:(NSNotification *)notification { [self detectMentions]; } - (void)detectMentions { // 支持中文、字母、数字、下划线和连字符 NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"@[\\w\\u4e00-\\u9fa5-]+" options:0 error:nil]; NSArray *matches = [regex matchesInString:self.text options:0 range:NSMakeRange(0, self.text.length)]; NSMutableArray *ranges = [NSMutableArray array]; for (NSTextCheckingResult *match in matches) { [ranges addObject:[NSValue valueWithRange:match.range]]; } self.mentionRanges = ranges; } - (BOOL)deleteMentionAtCursorPosition { NSRange selectedRange = self.selectedRange; if (selectedRange.location == 0) return NO; // 找出包含光标前一个位置的所有mention NSMutableArray *candidateMentions = [NSMutableArray array]; for (NSValue *rangeValue in _mentionRanges) { NSRange mentionRange = [rangeValue rangeValue]; if (NSLocationInRange(selectedRange.location - 2, mentionRange)) { [candidateMentions addObject:rangeValue]; } } if (candidateMentions.count == 0) return NO; // 找出最合适的mention(取范围最大的) NSValue *bestMatch = candidateMentions.firstObject; for (NSValue *rangeValue in candidateMentions) { if ([rangeValue rangeValue].length > [bestMatch rangeValue].length) { bestMatch = rangeValue; } } NSRange deleteRange = [bestMatch rangeValue]; NSString *deletedMention = [self.text substringWithRange:deleteRange]; // 先保存原始索引 NSUInteger originalIndex = [_mentionRanges indexOfObject:bestMatch]; // 执行删除 NSMutableString *text = [self.text mutableCopy]; [text deleteCharactersInRange:deleteRange]; self.text = text; // 更新selectedRange self.selectedRange = NSMakeRange(deleteRange.location, 0); // 更新其他mention的范围 [self updateMentionRangesAfterDeletion:deleteRange]; // 回调 if (self.mentionDeleteHandler) { self.mentionDeleteHandler(deletedMention, originalIndex); } return YES; } - (void)updateMentionRangesAfterDeletion:(NSRange)deletedRange { NSMutableArray *updatedRanges = [NSMutableArray array]; for (NSValue *rangeValue in _mentionRanges) { NSRange range = [rangeValue rangeValue]; if (NSEqualRanges(range, deletedRange)) { continue; // 跳过被删除的 } if (range.location > deletedRange.location) { range.location -= deletedRange.length; } [updatedRanges addObject:[NSValue valueWithRange:range]]; } _mentionRanges = updatedRanges; } - (void)deleteMentionAtIndex:(NSUInteger)index { if (index >= _mentionRanges.count) return; NSValue *rangeValue = _mentionRanges[index]; NSRange deleteRange = [rangeValue rangeValue]; NSString *deletedMention = [self.text substringWithRange:deleteRange]; // 执行删除 NSMutableString *text = [self.text mutableCopy]; [text deleteCharactersInRange:deleteRange]; self.text = text; // 更新selectedRange self.selectedRange = NSMakeRange(deleteRange.location, 0); // 更新其他mention的范围 [self updateMentionRangesAfterDeletion:deleteRange]; // 回调 if (self.mentionDeleteHandler) { self.mentionDeleteHandler(deletedMention, index); } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self]; UITextPosition *position = [self closestPositionToPoint:location]; NSUInteger index = [self offsetFromPosition:self.beginningOfDocument toPosition:position]; for (NSValue *rangeValue in self.mentionRanges) { NSRange mentionRange = [rangeValue rangeValue]; if (NSLocationInRange(index, mentionRange)) { if(self.text.length>=(mentionRange.location+mentionRange.length)){ NSString *mention = [self.text substringWithRange:mentionRange]; if (self.mentionTapHandler) { self.mentionTapHandler(mention); } } return; } } [super touchesEnded:touches withEvent:event]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } @end