web-dev-qa-db-ja.com

iOS7のUITextViewは、テキスト文字列の最後の行をクリップします

IOS7のUITextViewは本当に奇妙です。入力してUITextViewの最後の行を入力しているとき、スクロールビューは本来のように下部までスクロールせず、テキストが「クリッピング」されます。私は、そのclipsToBoundプロパティをNOに設定しようとしましたが、それでもテキストをクリップします。

「setContentOffset:animated」を呼び出したくないのは、その1つは非常にハッキングの解決策だからです。2つめは、カーソルがテキストビューの中央(垂直)にあると、不要なスクロールが発生するためです。

これがスクリーンショットです。

enter image description here

どんな助けも大歓迎です!

ありがとう!

54
ryan

この問題は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];
        }];
    }
}
93
davidisdk

私が見つけた解決策 here は、UITextViewを作成した後に1行の修正を追加することでした。

self.textview.layoutManager.allowsNonContiguousLayout = NO;

この1行は修正されました つの問題 iOS7で構文を強調表示するUITextViewベースのコードエディターを作成していました。

  1. 編集時にテキストを表示したままにするためのスクロール(この投稿の問題)
  2. キーボードを閉じた後、UITextViewが時々ジャンプする
  3. ビューをスクロールしようとするとUITextViewのランダムスクロールがジャンプする

キーボードが表示/非表示になったときに、UITextView全体のサイズを変更したことに注意してください。

21
gmyers

-textViewDidChangeSelection: UITextViewDelegateのデリゲートメソッドは次のようになります。

-(void)textViewDidChangeSelection:(UITextView *)textView {
    [textView scrollRangeToVisible:textView.selectedRange];
}
6
chris

これは、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が挿入されたテキストにアニメーション化しないというバグが発生していました。

2
cnotethegr8

これは、iOS 7のすべての典型的なUITextViewスクロール/キーボード関連の問題に対する決定的な回答です。そのクリーンで読みやすく、読みやすい使いやすく、メンテナンスが簡単で、簡単に再利用できます。

基本的なトリック:コンテンツのインセットではなく、UITextViewのサイズを変更するだけです!

これが実践的な例です。 Autolayoutを使用してNIB /ストーリーボードベースのUIViewControllerがあり、UITextViewがUIViewControllerのルートビュー全体を埋めていることは当然です。そうでない場合は、textViewBottomSpaceConstraintを必要に応じて変更する方法を調整する必要があります。

方法:


次のプロパティを追加します。

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewBottomSpaceConstraint;
@property (nonatomic) CGFloat textViewBottomSpaceConstraintFromNIB;

Interface BuilderでtextViewBottomSpaceConstraintを接続します(dont forget!

次に、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];
}
1
Wirsing

StoryBoardを使用している場合、AutoLayoutを(デフォルト)のままにし、UITextViewに上下の制約を設定しなかった場合にも、この動作が発生する可能性があります。ファイルインスペクターをチェックして、AutoLayoutの状態を確認してください...

0
Fabrice

以下は、davididskの最も優れたソリューションの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;
                    });
                }
            };
0
aumansoftware

この行により、テキストの最後の行が表示されません。

textView.scrollEnabled = false

これを削除して、何が起こるか見てください...

0
noobular
textView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 10.0, 0.0);

これはあなたの問題にも対処します

0
Dimitris