2013-09-16 2 views
9

Проблема: UITextView бесшумно меняет это contentSize в некоторых ситуациях.UITextView contentSize изменения и NSLayoutManager в iOS7

Простейший текстовый файл с большим текстом и клавиатурой. Просто добавьте выход 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} 

Похоже поведение UITextView было изменено много в iOS7. И некоторые вещи сейчас сломаны.

Узнав об этом, я обнаружил, что новое свойство моего текстового изменения также появилось в моем новом объекте 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 не изменяется между клавиатурой show/hide.

Вопрос: как предотвратить изменения контента?

+0

Я имею точно такой же вопрос :( – Legolas

ответ

7

Похоже, проблема заключается в макете по умолчанию UITextView. Я решил подклассифицировать его и посмотреть, где и почему начинается повторная компоновка. Но простое создание NSLayoutManager с настройками по умолчанию решило проблему.

Вот код (не идеально) из моего демонстрационного проекта (см. Вопрос). У _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]; 

MyTextView здесь подклассом UITextView см вопрос подробнее.

Для получения дополнительной информации см:

+0

К сожалению, это не работает для меня, что я делаю, как сказано здесь только с пользовательским текстовым хранилищем и contentSize по-прежнему изменяется по мере прокрутки представления. – Joshua

+0

Кажется, причина в том, что это работает, потому что вы установили, что высота текстового контейнера должна быть ограничена высотой представления. Это плохо работает для менеджера макета при вызове других методов, таких как 'lineFragmentUsedRectForGlyphAtIndex', поскольку он просто игнорирует любой внешний размер текстового контейнера. – Joshua

+2

После некоторого исследования прыжки, похоже, возникают, если высота текстового контейнера выше '9999999', поэтому все выше и в том числе 10 миллионов (' 10 000 000') вызвало скачок. Я не уверен, почему это число является особенным, но похоже, что это так. – Joshua

1

я встретил подобную ситуацию как УРС. Mine показывает с другой ошибкой, но по той же причине: свойство 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. Надеюсь, яблоко скоро исправит. Надеюсь, это полезно.

+0

Спасибо за ваш ответ. Но я сам не использую contentSize. 'UITextView' использует его (изменения размера контента вызывают« перескакивание текста »в' UITextView'). Поэтому мне нужно предотвратить изменения, а не получать правильное значение contentSize при чтении. – zxcat

+0

Другим обходным решением, которое я пробовал, было переопределение «setContentSize:» и не изменять значение, если выполняется макетирование. Но это действительно грязный хак. Поэтому теперь я использую обходное решение с созданием и установкой 'NSLayoutManager' в' UITextView' вместо использования встроенного менеджера макетов. Плохая сторона: такой 'UITextView' не может быть помещен в раскадровку/xib – zxcat

+0

@zxcat, я пробовал обходное решение layoutManger. Это работает и для меня. Но я ничего не понимаю. _textView = [[MyTextView alloc] initWithFrame: ..... Он не вызывает initWithFrame в классе MyTextView. Я должен выполнить начальную работу в viewDidLoad. Почему это? –

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]; 
    }]; 
} 
+0

Спасибо за исправление! Мое текстовое представление теперь снова прокручивается при редактировании и в последней отображаемой строке! – nicolas

Смежные вопросы