简体   繁体   English

UITextView contentSize更改和iOS7中的NSLayoutManager

[英]UITextView contentSize changes and NSLayoutManager in iOS7

The problem: UITextView silently changes it's contentSize in some situations. 问题:在某些情况下, UITextView默默地更改它的contentSize

The simplest case textView with large text and keyboard. 最简单的案例textView与大文本和键盘。 Just add UITextView outlet and set - viewDidLoad as: 只需添加UITextView outlet并将- 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);
}

Now showing and hiding keyboard will cause text jumps in some cases. 现在显示和隐藏键盘会在某些情况下导致文本跳转。

I've found the reason of jumping by subclass UITextView . 我找到了通过子类UITextView跳转的原因。 The only method in my subclass is: 我的子类中唯一的方法是:

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

And it show contentSize shrinks and expands on keyboard hide. 它显示contentSize缩小并扩展键盘隐藏。 Something like this: 像这样的东西:

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}

Looks like behavior of UITextView was changed a lot in iOS7. 看起来UITextView行为在iOS7中发生了很大变化。 And some things are broken now. 有些事情现在已经破裂了。

Discovering further I've found that new layoutManager property of my textView changes too. 进一步发现我发现layoutManagerlayoutManager属性也发生了变化。 There some interesting info in log now: 现在日志中有一些有趣的信息:

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

And next line with contentSize = {320, 885} contains Layout tree contents: ..., 2127 laid glyphs, 51 laid line fragments . 而contentSize = {320, 885}下一行包含Layout tree contents: ..., 2127 laid glyphs, 51 laid line fragments So it looks like some kind of autolayout tries to re-layout textView on keyboard show and changes contentSize even if layout is not finished yet. 所以看起来某种autolayout试图在键盘上重新布局textView并改变contentSize,即使布局还没有完成。 And it runs even if my textView is not changes between keyboard show/hide. 即使我的textView在键盘显示/隐藏之间没有变化,它也会运行。

The question is: how to prevent contentSize changes? 问题是:如何防止contentSize更改?

Looks like the problem is in default layoutManager of UITextView . 看起来问题出在UITextView默认layoutManager中。 I've decided to subclass it and see, where and why re-layout being initiated. 我已经决定对它进行子类化,并查看重新布局的启动位置和原因。 But simple creation of NSLayoutManager with default settings solved the problem. 但是使用默认设置简单创建NSLayoutManager解决了这个问题。

Here is code (not perfect) from my demo project (see in question). 这是我的演示项目中的代码(不完美)(参见问题)。 The _textView there was an outlet, so I remove it from superview. _textView有一个插座,所以我从_textView删除它。 This code is placed in - viewDidLoad : 此代码放在- 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 here is a subclass of UITextView , see question for details. 这里的MyTextViewUITextView的子类,详见问题。

For more info see: 有关详情,请参阅:

I met a similar situation as urs. 我遇到了类似的情况。 Mine shows with a different bug but due to the same reason: the contentSize property is silently changed by iOS7 incorrectly. 我的节目有不同的错误,但由于同样的原因:iOS7不正确地默默地更改了contentSize属性。 Here is how I work around it. 这是我如何解决它。 Its kinda a ugly fix. 它有点难看。 Whenever I need to use textView.contentSize, I calculate it by myself. 每当我需要使用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;
}

then you can just call this function to get the size: 然后你可以调用这个函数来获得大小:

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

then, don't do the following: 那么,不要做以下事情:

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

so, just use cont_size directly. 所以,直接使用cont_size。 I believe it's bug in iOS7 for now. 我相信它现在是iOS7中的错误。 Hopefully apple will fix it soon. 希望苹果能尽快修复它。 Hope this is helpful. 希望这是有帮助的。

Seems the bug in iOS7. 似乎是iOS7中的错误。 At the time of input text content area behavior is wired in iOS7, It works fine with lower iOS7 version. 在输入文本内容区域行为在iOS7中连接时,它适用于较低的iOS7版本。

I have added below delegate method of UITextView to resolved this issue : 我添加了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];
    }];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM