美文网首页
模仿微博#话题#和@功能实现

模仿微博#话题#和@功能实现

作者: 谁动了我的梦 | 来源:发表于2018-04-20 18:59 被阅读0次

    因为项目中有类似微博的话题和@功能,所以我们来说说类似于新浪微博话题功能的实现,当文字是”#话题#”这种格式时,该文字字体的颜色得变成蓝色。刚拿到这个内容猜测的时候应该用 UITextView 或 UITextField 去做,了解了思路就去百度了下,发现真的很少有这样的案例,得知 YYKIt 大神的 demo 中有这样的 demo   ,于是就去下载看看, 这个 YYkit 的链接GitHub - ibireme/YYKit: A collection of iOS components.    

    这个 demo 中有发布微博界面.

    于是想偷懒把整个 copy 过来一下,不用重复造轮子

    当我以为能用的时候我发现,这个删除是默认不能整体删除的, YY 大神都是用正则表达式去匹配内容,然后进行绑定和变色

    于是我就想在这个基础上看能不能改整体删除,改了半天还是不行,就在github 上搜索, 找了好久都是标题党,基本上都是模仿界面,根本没有实质性的内容,于是就在谷歌上面搜索,找了好久还是不多

    看了一下,发现新浪微博#话题#和@功能做的并不好,跟上面的情况一样,仔细发现,新浪微博#话题#和@功能虽然能变声并不能整体删除,这个我是测试过的,删除#再输入话题会出现正则匹配不正确的现象,@某人删除文字时候点击某人会出现查无此人显现, 不知道新浪微博测试和开放人员知不知道,YY 大神的 Demo 也是如如此,今天头条的发布是整体删除,给人感觉很好

    于是就谷歌,最后终于找了一篇,

    具体实现

    在实现过程中,我以AttributedString的颜色值为基准,用几个正则为查找工具,结合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 代理方法中,第一,实现第一次选中,第二次删除功能;第二,实现插入话题后,需要改变其他字符串的初始颜色,得在这个方法里面做个标志。

    textViewDidChange 代理方法中,实现 根据 shouldChangeTextInRange 方法中所得到的标志,设置字符串的初始颜色;

    textViewDidChangeSelection 代理方法中,实现让光标不能移动到话题里面;

    首先我定义了两个变量,插入了话题以后,继续在后面输入字符的话,字符颜色就跟话题颜色一样了。所以,我得用这两个变量来实现改变输入字符的初始颜色。

    /// 改变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数组

    *

    *  @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;

    }

    好吧,通过以上方法,基本输入、删除操作功能是实现了

    最后是上传, 上传是本地先定义一个数组topicArray

    然后在插入数据的时候往数组中插入一个对象

    删除数据的时候移除 topicArray 中的对象

    最后上传 textView的 text 内容 和数组中的对象

    显示:

     1:YYLabel 显示

     2:MLEmojiLabel 显示

    我是用了第二种MLEmojiLabel  ,YYLabel 没有深入研究,性能比MLEmojiLabel要好,等不忙了再换,

    因为我是上传了文本内容和对象给服务器,服务器给我类似的对象然后本地坐比对

    ,到此,爬坑算是结束了

    代码只是提供思路,没有做封装,也没有深入的去优化,希望用到的小伙伴能优化并做的更好,谢谢

    最后附上 demo https://github.com/986138497/UITextView-

    相关文章

      网友评论

          本文标题:模仿微博#话题#和@功能实现

          本文链接:https://www.haomeiwen.com/subject/ceuzkftx.html