UITextView в iOS7 обрезает последнюю строку текстовой строки
UITextView в iOS7 был действительно странным. Когда вы печатаете и вводите последнюю строку вашего UITextView, представление прокрутки не прокручивается до нижнего предела, как это должно быть, и оно приводит к "обрезанию" текста. Я попытался установить для его свойства clipsToBound значение NO, но оно все равно обрезает текст.
Я не хочу вызывать "setContentOffset: animated", потому что для одного: это очень хакерское решение... во-вторых: если курсор был посередине (вертикально) нашего текстового представления, это вызовет нежелательную прокрутку.
Вот скриншот.
Любая помощь будет принята с благодарностью!
Спасибо!
11 ответов
Проблема связана с iOS 7. В текстовом представлении делегата добавьте этот код:
- (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];
}];
}
}
Решение, которое я нашел здесь, было добавить исправление в одну строку после создания UITextView:
self.textview.layoutManager.allowsNonContiguousLayout = NO;
Эта строка исправила три проблемы, возникшие у меня при создании редактора кода на основе UITextView с подсветкой синтаксиса на iOS7:
- Прокрутка, чтобы держать текст в поле зрения при редактировании (выпуск этого поста)
- UITextView иногда прыгает вокруг после того, как распустить клавиатуру
- UITextView случайные переходы прокрутки при попытке прокрутки представления
Обратите внимание, я изменил размер всего UITextView, когда клавиатура отображается / скрывается.
Попробуйте реализовать -textViewDidChangeSelection:
метод делегата от UITextViewDelegate как это:
-(void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
}
Вот модифицированная версия выбранного ответа от davidisdk.
- (void)textViewDidChange:(UITextView *)textView {
NSRange selection = textView.selectedRange;
if (selection.location + selection.length == [textView.text length]) {
CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start];
CGFloat overflow = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top);
if (overflow > 0.0f) {
CGPoint offset = textView.contentOffset;
offset.y += overflow + 7.0f;
[UIView animateWithDuration:0.2f animations:^{
[textView setContentOffset:offset];
}];
}
} else {
[textView scrollRangeToVisible:selection];
}
}
Я получаю сообщение об ошибке, что, когда размер содержимого textView больше, чем границы и курсор находится за пределами экрана (например, с помощью клавиатуры и нажатия клавиши со стрелкой), textView не будет анимироваться для вставляемого текста.
Имхо, это окончательный ответ на все типичные проблемы UITextView, связанные с прокруткой / клавиатурой в iOS 7. Он чистый, его легко читать, легко использовать, легко обслуживать и легко использовать повторно.
Основная хитрость:просто измените размер UITextView, а не вставку содержимого!
Вот практический пример. Само собой разумеется, что у вас есть UIViewController на основе NIB/Storyboard, использующий Autolayout, а UITextView заполняет весь корневой вид в UIViewController. Если нет, вам придется адаптировать способ изменения textViewBottomSpaceConstraint к вашим потребностям.
Как:
Добавьте эти свойства:
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewBottomSpaceConstraint;
@property (nonatomic) CGFloat textViewBottomSpaceConstraintFromNIB;
Подключите textViewBottomSpaceConstraint в Интерфейсном Разработчике (не забывайте!)
Затем в viewDidLoad:
// Save the state of the UITextView's bottom constraint as set up in your NIB/Storyboard
self.textViewBottomSpaceConstraintFromNIB = self.textViewBottomSpaceConstraint.constant;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShowNotification:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHideNotification:)
name:UIKeyboardWillHideNotification
object:nil];
Добавьте эти методы для обработки изменения размера клавиатуры (спасибо https://github.com/brennanMKE/Interfaces/tree/master/Keyboarding - эти методы предоставлены brennan!):
- (void)keyboardWillShowNotification:(NSNotification *)notification {
CGFloat height = [self getKeyboardHeight:notification forBeginning:TRUE];
NSTimeInterval duration = [self getDuration:notification];
UIViewAnimationOptions curve = [self getAnimationCurve:notification];
[self keyboardWillShowWithHeight:height duration:duration curve:curve];
}
- (void)keyboardWillHideNotification:(NSNotification *)notification {
CGFloat height = [self getKeyboardHeight:notification forBeginning:FALSE];
NSTimeInterval duration = [self getDuration:notification];
UIViewAnimationOptions curve = [self getAnimationCurve:notification];
[self keyboardWillHideWithHeight:height duration:duration curve:curve];
}
- (NSTimeInterval)getDuration:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval duration;
NSValue *durationValue = [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
[durationValue getValue:&duration];
return duration;
}
- (CGFloat)getKeyboardHeight:(NSNotification *)notification forBeginning:(BOOL)forBeginning {
NSDictionary *info = [notification userInfo];
CGFloat keyboardHeight;
NSValue *boundsValue = nil;
if (forBeginning) {
boundsValue = [info valueForKey:UIKeyboardFrameBeginUserInfoKey];
}
else {
boundsValue = [info valueForKey:UIKeyboardFrameEndUserInfoKey];
}
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIDeviceOrientationIsLandscape(orientation)) {
keyboardHeight = [boundsValue CGRectValue].size.width;
}
else {
keyboardHeight = [boundsValue CGRectValue].size.height;
}
return keyboardHeight;
}
- (UIViewAnimationOptions)getAnimationCurve:(NSNotification *)notification {
UIViewAnimationCurve curve = [[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
switch (curve) {
case UIViewAnimationCurveEaseInOut:
return UIViewAnimationOptionCurveEaseInOut;
break;
case UIViewAnimationCurveEaseIn:
return UIViewAnimationOptionCurveEaseIn;
break;
case UIViewAnimationCurveEaseOut:
return UIViewAnimationOptionCurveEaseOut;
break;
case UIViewAnimationCurveLinear:
return UIViewAnimationOptionCurveLinear;
break;
}
return kNilOptions;
}
Наконец, добавьте эти методы для реагирования на уведомления клавиатуры и измените размер UITextView.
- (void)keyboardWillShowWithHeight:(CGFloat)height duration:(CGFloat)duration curve:(UIViewAnimationOptions)curve
{
CGFloat correctionMargin = 15; // you can experiment with this margin so the bottom text view line is not flush against the keyboard which doesn't look nice
self.textViewBottomSpaceConstraint.constant = height + correctionMargin;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
- (void)keyboardWillHideWithHeight:(CGFloat)height duration:(CGFloat)duration curve:(UIViewAnimationOptions)curve
{
self.textViewBottomSpaceConstraint.constant = self.textViewBottomSpaceConstraintFromNIB;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
Также добавьте эти методы, чтобы автоматически прокрутить туда, где пользователь нажал
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[textView scrollRangeToVisible:textView.selectedRange];
}
- (void)textViewDidChangeSelection:(UITextView *)textView
{
[textView scrollRangeToVisible:textView.selectedRange];
}
textView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 10.0, 0.0);
Это также решит вашу проблему
Эта строка приводит к тому, что последняя строка текста не отображается для меня:
textView.scrollEnabled = false
Попробуйте удалить это и посмотреть, что произойдет...
Вот версия MonoTouch самого прекрасного решения Давидидска (сверху).
TextView.SelectionChanged += (object sender, EventArgs e) => {
TextView.ScrollRangeToVisible(TextView.SelectedRange);
};
TextView.Changed += (object sender, EventArgs e) => {
CGRect line = TextView.GetCaretRectForPosition(TextView.SelectedTextRange.Start);
nfloat overflow = line.Y + line.Height -
(TextView.ContentOffset.Y +
TextView.Bounds.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.Animate(0.1,()=> {
TextView.ContentOffset = offset;
});
}
};
Если вы используете StoryBoard, то такое поведение также может произойти, если вы оставили AutoLayout включенным (как оно есть по умолчанию) и не установили ограничения сверху / снизу для вашего UITextView. Проверьте инспектора файлов, чтобы увидеть, каков ваш статус AutoLayout...
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Это решило проблему для меня
Установите theViewDelegate на "self" в вашем.m и используйте в своем.h, затем добавьте этот код в свой.m
Будет обрабатывать ОБА версии этого сбоя, возникающие при переходе к следующей строке с текстом (перенос или возврат каретки) и вводу... И переходу на следующую строку только с возвратом каретки и без ввода (этот код, в отличие от других код, будет прокручивать, чтобы показать, что мигающий курсор не обрезается в этом втором сценарии сбоя)
//!!!*!!****!*!**!*!*!!!MAKE SURE YOU SET DELEGATE AND USE THE <UITEXTVIEWDELEGATE>
-(void)textViewDidChange:(UITextView *)textView {
[theTextView scrollRangeToVisible:[textView selectedRange]];//resizing textView frame causes text itself "content frame?" to still go below the textview frame and get clipped... auto-scrolling must be implimented. (iOS7 bug)
}
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if (([text isEqualToString:@"\n"]) && (range.location == textView.text.length)) {//"return" at end of textView
[textView scrollRectToVisible:CGRectMake(5,5,5,999999999999999999) animated:NO];//for some reason the textViewDidChange auto scrolling doesnt work with a carriage return at the end of your textView... so I manually set it INSANELY low (NOT ANIMATED) here so that it automagically bounces back to the proper position before interface refreshes when textViewDidChange is called after this.
}
return YES;
}