iOS 7.1 UITextView在新行后仍然不能滚动到cursor / caret

自iOS 7以来, UITextView不会自动滚动到光标,因为用户键入stream向新行的文本。 这个问题在SO和其他地方都有很好的logging。 对我来说,这个问题在iOS 7.1中仍然存在。 我究竟做错了什么?

在这里输入图像说明

我安装了Xcode 5.1和iOS 7.1。 我正在使用自动布局。

以下是我如何将文本视图的内容定位在键盘上:

 - (void)keyboardUp:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; keyboardRect = [self.view convertRect:keyboardRect fromView:nil]; UIEdgeInsets contentInset = self.textView.contentInset; contentInset.bottom = keyboardRect.size.height; self.textView.contentInset = contentInset; } 

我曾经尝试过:我已经尝试了许多针对这个问题的解决scheme,因为它与iOS 7有关。我尝试过的所有解决scheme对于显示属性string的文本视图似乎都不太好。 在以下三个步骤中,我概述了SO( https://stackoverflow.com/a/19277383/1239263 )上的最新推荐答案是如何响应用户第一次点击返回键的。

(1.)文本视图成为viewDidLoad的第一个响应者。 滚动到光标所在文本视图的底部。

在这里输入图像说明

(2.)在input单个字符之前,点击键盘上的返回键。 脱字符号消失在视线之外。

在这里输入图像说明

(3.)然而,再次点击返回键似乎使情况正常化。 (注:删除后面的新行,但是,使脱字符号再次消失)。

在这里输入图像说明

改进了UITextView后代类的解决scheme的代码:

 #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) #define is_iOS7 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") #define is_iOS8 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") @implementation MyTextView { BOOL settingText; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTextViewDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self]; } return self; } - (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated { CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end]; rect.size.height += textView.textContainerInset.bottom; [textView scrollRectToVisible:rect animated:animated]; } - (void)handleTextViewDidChangeNotification:(NSNotification *)notification { if (notification.object == self && is_iOS7 && !is_iOS8 && !settingText) { UITextView *textView = self; if ([textView.text hasSuffix:@"\n"]) { [CATransaction setCompletionBlock:^{ [self scrollToCaretInTextView:textView animated:NO]; }]; } else { [self scrollToCaretInTextView:textView animated:NO]; } } } - (void)setText:(NSString *)text { settingText = YES; [super setText:text]; settingText = NO; } 

注意,在蓝牙键盘上按下向下键时,它不起作用。

在下列情况下,一个可靠的解决scheme应该可以维持

(1.)显示属性string的文本视图

(2.)通过点击键盘上的返回键创build一条新线

(3.)通过input溢出到下一行的文本创build一个新行

(4.)复制和粘贴文本

(5.) 第一次点击返回键创build一条新线(参见OP中的3个步骤)

(6.)设备旋转

(7.)有些情况下,我想不出你会…

为了在iOS 7.1中满足这些要求,似乎仍然需要手动滚动到插入符号。

当文本视图委托方法textViewDidChange:被调用时,通常会看到手动滚动到插入符的解决scheme。 但是,我发现这种技术不符合上述情况#5。 在滚动到插入符号前,即使调用layoutIfNeeded也没有帮助。 相反,我必须滚动到CATransaction完成块内的插入符:

 // this seems to satisfy all of the requirements listed above–if you are targeting iOS 7.1 - (void)textViewDidChange:(UITextView *)textView { if ([textView.text hasSuffix:@"\n"]) { [CATransaction setCompletionBlock:^{ [self scrollToCaretInTextView:textView animated:NO]; }]; } else { [self scrollToCaretInTextView:textView animated:NO]; } } 

为什么这个工作? 我不知道。 你将不得不问一个苹果工程师。

为了完整起见,以下是与我的解决scheme相关的所有代码:

 #import "ViewController.h" @interface ViewController () <UITextViewDelegate> @property (weak, nonatomic) IBOutlet UITextView *textView; // full-screen @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSString *string = @"All work and no play makes Jack a dull boy.\n\nAll work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy."; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName: [UIFont fontWithName:@"Verdana" size:30.0]}]; self.textView.attributedText = attrString; self.textView.delegate = self; self.textView.backgroundColor = [UIColor yellowColor]; [self.textView becomeFirstResponder]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardIsUp:) name:UIKeyboardDidShowNotification object:nil]; } // helper method - (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated { CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end]; rect.size.height += textView.textContainerInset.bottom; [textView scrollRectToVisible:rect animated:animated]; } - (void)keyboardIsUp:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; keyboardRect = [self.view convertRect:keyboardRect fromView:nil]; UIEdgeInsets inset = self.textView.contentInset; inset.bottom = keyboardRect.size.height; self.textView.contentInset = inset; self.textView.scrollIndicatorInsets = inset; [self scrollToCaretInTextView:self.textView animated:YES]; } - (void)textViewDidChange:(UITextView *)textView { if ([textView.text hasSuffix:@"\n"]) { [CATransaction setCompletionBlock:^{ [self scrollToCaretInTextView:textView animated:NO]; }]; } else { [self scrollToCaretInTextView:textView animated:NO]; } } @end 

如果您发现这种情况无效,请告诉我。

我通过获得脱字符的实际位置并对其进行调整来解决这个问题,这里是我的方法:

 - (void) alignTextView:(UITextView *)textView withAnimation:(BOOL)shouldAnimate { // where the blinky caret is CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start]; CGFloat offscreen = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top); CGPoint offsetP = textView.contentOffset; offsetP.y += offscreen + 3; // 3 px -- margin puts caret 3 px above bottom if (offsetP.y >= 0) { if (shouldAnimate) { [UIView animateWithDuration:0.2 animations:^{ [textView setContentOffset:offsetP]; }]; } else { [textView setContentOffset:offsetP]; } } } 

如果您只需在用户按下返回/input后定位,请尝试:

 - (void) textViewDidChange:(UITextView *)textView { if ([textView.text hasSuffix:@"\n"]) { [self alignTextView:textView withAnimation:NO]; } } 

请让我知道这对你有没有用!

我无法find原始的源代码,但它可以在iOS7.1上运行

  - (void)textViewDidChangeSelection:(UITextView *)textView { if ([textView.text characterAtIndex:textView.text.length-1] != ' ') { textView.text = [textView.text stringByAppendingString:@" "]; } NSRange range0 = textView.selectedRange; NSRange range = range0; if (range0.location == textView.text.length) { range = NSMakeRange(range0.location - 1, range0.length); } else if (range0.length > 0 && range0.location + range0.length == textView.text.length) { range = NSMakeRange(range0.location, range0.length - 1); } if (!NSEqualRanges(range, range0)) { textView.selectedRange = range; } } 

有人已经做了一个子类,解决UITextView所有滚动相关的问题。 实现不能简单 – 将UITextViewPSPDFTextView子类PSPDFTextView

一个关于它的post,显示了什么是固定的(与漂亮的GIFanimation)在这里: 修复iOS 7上的UITextView

git在这里: PSPDFTextView