UITextView contentSize更改和iOS7中的NSLayoutManager

时间:2013-09-16 10:51:09

标签: ios uitextview ios7 nslayoutmanager

问题:UITextView在某些情况下默默地更改contentSize

最简单的案例textView,带有大文本和键盘。只需添加UITextView插座并将- viewDidLoad设置为:

- (void)viewDidLoad {
    [super viewDidLoad];
    // expand default "Lorem..."
    _textView.text = [NSString stringWithFormat:@"1%@\n\n2%@\n\n3%@\n\n4%@\n\n5", _textView.text, _textView.text, _textView.text, _textView.text];
    _textView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
    _textView.contentInset = UIEdgeInsetsMake(0, 0, 216, 0);
}

现在显示和隐藏键盘会导致文本跳转。

我找到了按子类UITextView跳转的原因。我的子类中唯一的方法是:

- (void)setContentSize:(CGSize)contentSize {
    NSLog(@"CS: %@", NSStringFromCGSize(contentSize));
    [super setContentSize:contentSize];
}

它显示contentSize缩小并扩展键盘隐藏。像这样:

013-09-16 14:40:27.305 textView-bug2[11087:a0b] CS: {320, 651}
2013-09-16 14:40:27.313 textView-bug2[11087:a0b] CS: {320, 885}
2013-09-16 14:40:27.318 textView-bug2[11087:a0b] CS: {320, 902}

在iOS7中看起来UITextView的行为发生了很大变化。现在有些事情已经破裂。

进一步发现我发现textView的新layoutManager属性也发生了变化。现在在日志中有一些有趣的信息:

2013-09-16 14:41:59.352 textView-bug2[11115:a0b] CS: {320, 668}
<NSLayoutManager: 0x899e800>
    1 containers, text backing has 2129 characters
    Currently holding 2129 glyphs.
    Glyph tree contents:  2129 characters, 2129 glyphs, 3 nodes, 96 node bytes, 5440 storage bytes, 5536 total bytes, 2.60 bytes per character, 2.60 bytes per glyph
    Layout tree contents:  2129 characters, 2129 glyphs, 532 laid glyphs, 13 laid line fragments, 4 nodes, 128 node bytes, 1048 storage bytes, 1176 total bytes, 0.55 bytes per character, 0.55 bytes per glyph, 40.92 laid glyphs per laid line fragment, 90.46 bytes per laid line fragment

包含contentSize = {320, 885}的下一行包含Layout tree contents: ..., 2127 laid glyphs, 51 laid line fragments。所以看起来某种autolayout试图在键盘上重新布局textView并改变contentSize,即使布局还没有完成。即使我的textView在键盘显示/隐藏之间没有变化,它也会运行。

问题是:如何防止contentSize更改?

3 个答案:

答案 0 :(得分:7)

看起来问题出在UITextView的默认layoutManager中。我已经决定对它进行子类化,并查看重新布局的启动位置和原因。但是使用默认设置简单创建NSLayoutManager可以解决问题。

这是我的演示项目中的代码(不完美)(参见问题)。 _textView有一个出口,所以我从superview中删除它。此代码放在- viewDidLoad

NSTextStorage* textStorage = [[NSTextStorage alloc] initWithString:_textView.text];
NSLayoutManager* layoutManager = [NSLayoutManager new];
[textStorage addLayoutManager:layoutManager];
_textContainer = [[NSTextContainer alloc] initWithSize:self.view.bounds.size];
[layoutManager addTextContainer:_textContainer];
[_textView removeFromSuperview];    // remove original textView
_textView = [[MyTextView alloc] initWithFrame:self.view.bounds 
                                textContainer:_textContainer];
[self.view addSubview:_textView];

MyTextView此处是UITextView的子类,详见问题。

有关详细信息,请参阅:

答案 1 :(得分:1)

我遇到了与她相似的情况。我的节目有不同的错误,但由于同样的原因:iOS7不正确地默默地更改了contentSize属性。 这是我如何解决它。它有点难看。 每当我需要使用textView.contentSize时,我自己计算它。

-(CGSize)sizeOfText:(NSString *)textToMesure widthOfTextView:(CGFloat)width withFont:(UIFont*)font
{
    CGSize size = [textToMesure sizeWithFont:font constrainedToSize:CGSizeMake(width-20.0, FLT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
    return size;
}

然后你可以调用这个函数来获得大小:

CGSize cont_size =   [self sizeOfText:self.text widthOfTextView:self.frame.size.width withFont:[UIFont systemFontOfSize:15]];

然后,请勿执行以下操作:

self.contentSize = cont_size;// it causes iOS halt occasionally.

所以,只需直接使用cont_size。 我相信它现在是iOS7中的错误。希望苹果能尽快修复它。 希望这有用。

答案 2 :(得分:1)

似乎是iOS7中的错误。在iOS7中输入文本内容区域行为时,它可以在较低的iOS7版本下正常工作。

我在UITextView下面添加了委托方法来解决这个问题:

- (void)textViewDidChange:(UITextView *)textView {
CGRect line = [textView caretRectForPosition:
    textView.selectedTextRange.start];
CGFloat overflow = line.origin.y + line.size.height
    - ( textView.contentOffset.y + textView.bounds.size.height
    - textView.contentInset.bottom - textView.contentInset.top );
if ( overflow > 0 ) {
// We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it)
// Scroll caret to visible area
    CGPoint offset = textView.contentOffset;
    offset.y += overflow + 7; // leave 7 pixels margin
// Cannot animate with setContentOffset:animated: or caret will not appear
    [UIView animateWithDuration:.2 animations:^{
        [textView setContentOffset:offset];
    }];
}
相关问题