web-dev-qa-db-ja.com

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
25
Mikt25

これは明らかにiOS 7のバグです。 Appleが修正するまでの回避策を示します。この回避策は、基本的にUITextViewNSTextStorageを最初から作成してNSLayoutManagerをインスタンス化します。= Apple 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];
}
27
RawMean

これは私にとってiOS7で機能します。

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

IOS 7には、問題を説明できる2つの問題があります。

  • IOS 7ではcontentOffsetが常に最新であるとは限りません。
  • scrollRangeToVisible:テキストビューの最後にある空の行にスクロールしません。

解決策は次のとおりです。

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

これを試して:

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

Swiftを使用している人のために、RawMeanと同じ回答をここに投稿します(ありがとうございます!)。私がこれを書いたとき(2014年12月)、問題はまだiOS 8.1に存在し、彼のソリューションは完全に動作します...

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)
1
Steve S.

それは私にとってはうまくいきます。参照: ITextView 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];
}
0
leo5th

Swift 2.0-IOS 8

これは基本的には上記のSwift 2.0バージョンの dkltの答え です。以前はscrollEnabledの2行なしで同じ方法を使用していました。時間は正常に機能しますが、scrollToBottom()がほぼ同時に同時に連続して呼び出された場合、機能しないことがあります。

scrollEnabledの2行はあまり意味がありませんが、それらを追加した後、メソッドは機能します一貫性があります

注:dkltの回答のコメントで提案されているように、scrollEnabledの前後のさまざまな位置にscrollRangeTovisibleの2行を配置しようとしました...

Dkltの元のソリューションのみが機能します。残りはしません。

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
}
0
interceptwind

基本的に、setScrollEnabled = YESは、layoutSubviewsが呼び出される前に設定する必要があります。それは私のために働いた。

0
Shilpi