ibireme / YYText

Powerful text framework for iOS to display and edit rich text.
MIT License
8.86k stars 1.7k forks source link

YYLabel 在TableViewCell 中 没有刷新 #104

Closed lexiaoyao20 closed 8 years ago

lexiaoyao20 commented 8 years ago

我的初始化代码:

NSInteger fontsize = 14;
    _titleMessageLabel = [YYLabel new];
    _titleMessageLabel.size = CGSizeMake(180, 21);
    _titleMessageLabel.font = [UIFont systemFontOfSize:fontsize];
    _titleMessageLabel.textColor = [themeMgr colorForKey:@"LabelTextColor2"];
    _titleMessageLabel.lineBreakMode = NSLineBreakByTruncatingTail;
    _titleMessageLabel.numberOfLines = 1;
    _titleMessageLabel.displaysAsynchronously = YES;
    _titleMessageLabel.ignoreCommonProperties = YES;
    _titleMessageLabel.fadeOnAsynchronouslyDisplay = NO;
    _titleMessageLabel.fadeOnHighlight = NO;
    _titleMessageLabel.textVerticalAlignment = YYTextVerticalAlignmentTop;
    _titleMessageLabel.textParser = [FMEmojiParse new]; //解析表情
    [self.contentView addSubview:_titleMessageLabel];

然后在 cellForRowAtIndexPath 方法里面设置Model

self.nameLabel.text = messageItem.ShowNickName;
    self.titleMessageLabel.text = messageItem.MessageBody;
    NSLog(@"nickName:%@ titleMessageLabel.text:%@ messageBody:%@",messageItem.ShowNickName,self.titleMessageLabel.text, messageItem.MessageBody);

上面我特意打印了log,输出的self.titleMessageLabel.text跟显示的不一样,不知道是不是YYLabel没有刷新。

下面附上效果图和打印日志: 效果图

打印日志如下: 日志

lexiaoyao20 commented 8 years ago

我使用的YYKit版本是 - YYKit (0.9.7): 另外,用UILabel不会有这个问题 会不会和TableView的重用机制有关呢?

ibireme commented 8 years ago

ignoreCommonProperties 这个属性设为 NO 应该就没问题了。 这个属性是 YES 时,Label 只会用 textLayout 来显示,而忽略了 text、attributedText、parser 等属性。

lexiaoyao20 commented 8 years ago

还真是这样,非常感谢!!! 看来我还得多多深入的看你这套框架才行

lexiaoyao20 commented 8 years ago
//    self.titleMessageLabel.text = messageItem.MessageBody;
    self.titleMessageLabel.attributedText = [[NSAttributedString alloc] initWithString:messageItem.MessageBody];

ignoreCommonProperties 这个属性设置未NO,还是有这个问题,但是cellForRowAtIndexPath 方法中设置attributedText能显示正常

ibireme commented 8 years ago

能给一下更详细的代码吗?比如那个 textParser? 或者,你在 [YYLabel setText:] 这个方法里下个断点走一下看看。

lexiaoyao20 commented 8 years ago

这是我的textParser的代码:

@implementation FMEmojiParse

- (BOOL)parseText:(NSMutableAttributedString *)text selectedRange:(NSRangePointer)range {
    if (text.length == 0) {
        return NO;
    }
    NSArray *matches = [[FMWeiBoHelper regexEmoticon] matchesInString:text.string options:kNilOptions range:text.rangeOfAll];
    if (matches.count == 0) return NO;

    NSRange selectedRange = range ? *range : NSMakeRange(0, 0);
    NSUInteger cutLength = 0;
    for (NSUInteger i = 0, max = matches.count; i < max; i++) {
        NSTextCheckingResult *one = matches[i];
        NSRange oneRange = one.range;
        if (oneRange.length == 0) continue;
        oneRange.location -= cutLength;
        NSString *subStr = [text.string substringWithRange:oneRange];
        NSString *imageName = [FMWeiBoHelper emojiNameFromDescription:subStr];
        UIImage *emoticon = [FMWeiBoHelper imageNamed:imageName];
        if (!emoticon) continue;

        CGFloat fontSize = 12; // CoreText default value
        CTFontRef font = (__bridge CTFontRef)([text attribute:NSFontAttributeName atIndex:oneRange.location]);
        if (font) fontSize = CTFontGetSize(font);
        NSMutableAttributedString *atr = [NSAttributedString attachmentStringWithEmojiImage:emoticon fontSize:fontSize];
        [atr setTextBackedString:[YYTextBackedString stringWithString:subStr] range:NSMakeRange(0, atr.length)];
        [text replaceCharactersInRange:oneRange withString:atr.string];
        [text removeDiscontinuousAttributesInRange:NSMakeRange(oneRange.location, atr.length)];
        [text addAttributes:atr.attributes range:NSMakeRange(oneRange.location, atr.length)];
        selectedRange = [self _replaceTextInRange:oneRange withLength:atr.length selectedRange:selectedRange];
        cutLength += oneRange.length - 1;
    }
    if (range) *range = selectedRange;

    return YES;
}

// correct the selected range during text replacement
- (NSRange)_replaceTextInRange:(NSRange)range withLength:(NSUInteger)length selectedRange:(NSRange)selectedRange {
    // no change
    if (range.length == length) return selectedRange;
    // right
    if (range.location >= selectedRange.location + selectedRange.length) return selectedRange;
    // left
    if (selectedRange.location >= range.location + range.length) {
        selectedRange.location = selectedRange.location + length - range.length;
        return selectedRange;
    }
    // same
    if (NSEqualRanges(range, selectedRange)) {
        selectedRange.length = length;
        return selectedRange;
    }
    // one edge same
    if ((range.location == selectedRange.location && range.length < selectedRange.length) ||
        (range.location + range.length == selectedRange.location + selectedRange.length && range.length < selectedRange.length)) {
        selectedRange.length = selectedRange.length + length - range.length;
        return selectedRange;
    }
    selectedRange.location = range.location + length;
    selectedRange.length = 0;
    return selectedRange;
}

@end

基本上是照搬的你Demo的代码

看了下 setText 和 setAttributedText 两个方法,有一个不同之处是setAttributedText 方法会对 _innerText 重新赋值

ibireme commented 8 years ago

仔细看了一下这块儿的代码,没发现什么问题啊。。我也一直复现不了这个问题。。

你能试试在 [YYLabel setText:] 这个方法里加个断点看看吗,看看这个方法里面都执行了哪些步骤?

lexiaoyao20 commented 8 years ago

数据项要有7,8项以上的数据才会有这个问题(即超过TableView的默认缓存cell个数)

我改了下 setText 的代码, 下面这个改运行是正常的:

- (void)setText:(NSString *)text {
    if (_text == text || [_text isEqualToString:text]) return;
    _text = text.copy;
    BOOL needAddAttributes = _innerText.length == 0 && text.length > 0;
//    [_innerText replaceCharactersInRange:NSMakeRange(0, _innerText.length) withString:text ? text : @""];
    _innerText = [[NSMutableAttributedString alloc] initWithString:text];
//    if (needAddAttributes) {
        _innerText.font = _font ? _font : [self _defaultFont];
        _innerText.color = _textColor;
        _innerText.shadow = [self _shadowFromProperties];
        _innerText.alignment = _textAlignment;
        switch (_lineBreakMode) {
            case NSLineBreakByWordWrapping:
            case NSLineBreakByCharWrapping:
            case NSLineBreakByClipping: {
                _innerText.lineBreakMode = _lineBreakMode;
            } break;
            case NSLineBreakByTruncatingHead:
            case NSLineBreakByTruncatingTail:
            case NSLineBreakByTruncatingMiddle: {
                _innerText.lineBreakMode = NSLineBreakByWordWrapping;
            } break;
            default: break;
        }
//    }
    if ([_textParser parseText:_innerText selectedRange:NULL]) {
        [self _updateOuterTextProperties];
    }
    if (!_ignoreCommonProperties) {
        if (_displaysAsynchronously && _clearContentsBeforeAsynchronouslyDisplay) {
            [self _clearContents];
        }
        [self _setLayoutNeedUpdate];
        [self _endTouch];
    }
}
lexiaoyao20 commented 8 years ago

我特意打印了下日志:

2016-01-07 18:32:42.180 Follow me[8840:2594372] _innerText:Aaaa{
    CTForegroundColor = "<CGColor 0x7f9793e425f0> [<CGColorSpace 0x7f9791530960> (kCGColorSpaceDeviceRGB)] ( 0.4 0.4 0.4 1 )";
    CTRunDelegate = "<CTRunDelegate 0x7f9794039590 [0x10da477b0]>";
    NSColor = "UIDeviceRGBColorSpace 0.4 0.4 0.4 1";
    NSFont = "<UICTFont: 0x7f97938edfc0> font-family: \".SFUIText-Regular\"; font-weight: normal; font-style: normal; font-size: 14.00pt";
    NSParagraphStyle = "Alignment 0, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0";
    YYTextAttachment = "<YYTextAttachment: 0x7f9794035670>";
    YYTextBackedString = "<YYTextBackedString: 0x7f97940395f0>";
} 
 text:Aaaa

Aaaa 这个Text 并不包含表情,但是 innerText 里却有 YYTextAttachment,我怀疑replaceCharactersInRange 这个方法并不会改变它原有的 attachment 这些属性,所有显示就乱了。

lexiaoyao20 commented 8 years ago

两种修改建议:

  1. 按我上面的代码,每次重新生成一个 _innerText
  2. 每次parseText之前,清理掉已有的 textAttachment

你看这样有没有问题?

ibireme commented 8 years ago

在 if (needAddAttributes) 上面加上一行:

[_innerText removeDiscontinuousAttributesInRange:NSMakeRange(0, _innerText.length)];

逻辑上应该就对了。

这个能解决你的问题吗?如果 OK 我就更新下代码。

lexiaoyao20 commented 8 years ago

用你上面的代码,能够解决问题

ibireme commented 8 years ago

代码已更新