UITextView的contentSize更改和iOS7中的NSLayoutManager

问题:在某些情况下, UITextView默默地改变它的contentSize

最简单的情况下textView与大文本和键盘。 只需添加UITextViewsockets,并设置- 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} 

看起来UITextView行为在iOS7中被改变了很多。 现在有些东西被打破了

进一步发现我发现我的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 。 所以它看起来像某种自动布局尝试重新布局键盘显示textView和更改contentSize即使布局尚未完成。 即使我的textView不在键盘显示/隐藏之间改变它运行。

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

看起来像在UITextView默认layoutManager中的问题。 我已经决定将其子类化,看看在哪里以及为什么要重新布局。 但使用默认设置简单创buildNSLayoutManager解决了这个问题。

这里是我的演示项目的代码(不完美)(见问题)。 _textView有一个出口,所以我把它从_textView删除。 这段代码放在- 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]; 

这里的MyTextViewUITextView一个子类,详见问题。

更多信息请参阅:

  • 关于iOS中的文字处理
  • 使用文本工具包绘制和pipe理文本
  • 适用于iOS的NSLayoutManager类参考

我遇到了类似于美国的情况。 我显示了一个不同的错误,但由于相同的原因:contentSize属性被iOS7默默地改变了。 这是我如何解决这个问题。 这是一个丑陋的修复。 每当我需要使用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的bug。 希望苹果能尽快修复它。 希望这是有帮助的。

看来在iOS7中的错误。 在iOS7中input文本内容区域行为的时候,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]; }]; }