滚动到iOS 7中不稳定的UITextView底部

下面的代码将在iOS 7.0中正常工作。 在iOS 7中,UITextView正在更新时,滚动会变得不稳定和不稳定。 我不确定这是否是iOS 7中的错误,或者我做错了什么。

TestController.h

//TODO: Add UITextView in storyboard and tie to textView outlet #define MAX_TEXT_VIEW_CHARACTERS 1000 @interface TestController : UIViewController { NSMutableString *_outputText; NSTimer *_outputTimer; } @property (strong, nonatomic) IBOutlet UITextView *textView; @end 

TestController.m

 @implementation TestController @synthesize textView; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _outputText = [NSMutableString stringWithCapacity:MAX_TEXT_VIEW_CHARACTERS]; _outputTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(outputLine:) userInfo:nil repeats:YES]; } -(void)outputLine:(NSTimer *) theTimer { static int i = 0; //Run this 100 times if (i > 99) { [_outputTimer invalidate]; return; } [self outputToScreen:[NSString stringWithFormat:@"Some string %d\r", ++i]]; } -(void)outputToScreen:(NSString *)str { if (!str || !str.length) return; //Nothing to output NSInteger outputTextSize = _outputText.length; [_outputText appendString:str]; if (outputTextSize > MAX_TEXT_VIEW_CHARACTERS) [_outputText deleteCharactersInRange:NSMakeRange(0, outputTextSize - MAX_TEXT_VIEW_CHARACTERS)]; self.textView.text = _outputText; [self scrollOutputToBottom]; } -(void)scrollOutputToBottom { CGPoint p = [textView contentOffset]; [textView setContentOffset:p animated:NO]; [textView scrollRangeToVisible:NSMakeRange([textView.text length], 0)]; } @end 

这显然是一个iOS 7的错误。 这是一个解决方法,直到苹果修复它。 解决方法基本上是通过从头创build一个NSTextStorageNSLayoutManager实例化一个UITextView 。 苹果公司一定忘了在UITextView初始化方法中初始化一些东西。 我提交了一个错误报告,我希望你也能做到。

 // ios7 bug fix // check if the device is running iOS 7.0 or later NSString *reqSysVer = @"7.0"; NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending); if (osVersionSupported) { NSTextStorage* textStorage = [[NSTextStorage alloc] init]; NSLayoutManager* layoutManager = [NSLayoutManager new]; [textStorage addLayoutManager:layoutManager]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.view.bounds.size]; [layoutManager addTextContainer:textContainer]; yourTextView = [[UITextView alloc] initWithFrame:someFrameForYourTextView textContainer:textContainer]; // if using ARC, remove these 3 lines [textContainer release]; [layoutManager release]; [textStorage release]; } else { yourTextView = [[UITextView alloc] initWithFrame:someFrameForYourTextView]; } 

这在iOS7中适用于我。

 -(void) scrollToBottom { [textView scrollRangeToVisible:NSMakeRange([textView.text length], 0)]; [textView setScrollEnabled:NO]; [textView setScrollEnabled:YES]; } 

iOS 7中有两个问题可以解释你的问题:

  • contentOffset在iOS 7中并不总是最新的。
  • scrollRangeToVisible:不会滚动到文本视图结尾的空行。

解决scheme可能是:

 -(void)scrollOutputToBottom { CGRect caretRect = [textView caretRectForPosition:textView.endOfDocument]; [textView scrollRectToVisible:caretRect animated:NO]; } 

尝试这个:

 // Don't forget to set textView's delegate -(void)textViewDidChangeSelection:(UITextView *)textView { [textView scrollRangeToVisible:NSMakeRange([textView.text length], 0)]; } 

对于那些使用Swift的人,我在这里发布与RawMean相同的答案(再次感谢!)。 正如我写的(2014年12月),这个问题仍然存在于iOS 8.1和他的解决scheme完美的工作…

 var textView: UITextView! var textStorage: NSTextStorage! var layoutManager: NSLayoutManager! var textContainer: NSTextContainer! override func viewDidLoad() { textStorage = NSTextStorage() layoutManager = NSLayoutManager() textStorage.addLayoutManager(layoutManager) let newTextViewRect = view.bounds let containerSize = CGSize(width: newTextViewRect.width, height: CGFloat.max) textContainer = NSTextContainer(size: containerSize) layoutManager.addTextContainer(textContainer) textView = UITextView(frame: newTextViewRect, textContainer: textContainer) textView.delegate = self view.addSubview(textView) } override func viewDidLayoutSubviews() { textView.frame = view.bounds } 

和我使用scrollRangeToVisible方法滚动顺利在文本添加底部…

 let length = countElements(textView.text) let range:NSRange = NSMakeRange(length - 1, 1) textView.scrollRangeToVisible(range) 

基本上setScrollEnabled = YES需要在layoutSubviews被调用之前设置。 它为我工作。

这对我行得通。 参考: UITextView setText不应该跳转到ios8的顶部

 self.textView.layoutManager.allowsNonContiguousLayout = NO; self.textView.text = fileContent; if(fileContent.length > 1) { NSRange range = NSMakeRange(self.textView.text.length - 1, 1); [self.textView scrollRangeToVisible:range]; } 

Swift 2.0 – IOS 8

这基本上是上面dklt的答案的Swift 2.0版本。 以前我没有使用scrollEnabled的2行使用相同的方法。 大多数情况下,它工作正常。 但是,几乎同时快速连续调用scrollToBottom()时,有时不起作用。

scrollEnabled的2行没有多大意义,但是在添加它们之后,该方法始终如一地工作

注意:我试图把scrollEnabled的两行放在scrollEnabled之前或之后的不同位置,就像在dklt的回答的注释中所build议的那样。

只有dklt的原始解决scheme适用于我。 其余的不是。

 func scrollToBottom() { let range:NSRange = NSMakeRange(self.textView.text.characters.count - 1, 1) self.textView.scrollRangeToVisible(range) self.textView.scrollEnabled = false self.textView.scrollEnabled = true } 

请尝试这个解决scheme

 -(void) scrollToBottom { [textView setContentOffset:CGPointMake(0.0, textView.contentSize.height) animated:YES]; }