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

因为项目中有类似微博的话题和@功能,所以我们来说说类似于新浪微博话题功能的实现,当文字是”#话题#”这种格式时,该文字字体的颜色得变成蓝色。刚拿到这个内容猜测的时候应该用 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-

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容