54

UITextView in iOS7 has been really weird. As you type and are entering the last line of your UITextView, the scroll view doesn't scroll to the bottom like it should and it causes the text to be "clipped". I've tried setting it's clipsToBound property to NO but it still clips the text.

I don't want to call on "setContentOffset:animated" because for one: that's very hacky solution.. secondly: if the cursor was in the middle (vertically) of our textview, it'll cause unwanted scrolling.

Here's a screenshot.

enter image description here

Any help would be greatly appreciated!

Thanks!

4

11 回答 11

93

问题是由于 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];
        }];
    }
}
于 2013-10-09T16:17:15.923 回答
21

我在这里找到的解决方案是在创建 UITextView 后添加一行修复:

self.textview.layoutManager.allowsNonContiguousLayout = NO;

这一行解决了我在 iOS7 上创建一个基于 UITextView 的带有语法高亮的代码编辑器的三个问题:

  1. 编辑时滚动以保持文本可见(本帖的问题)
  2. 关闭键盘后 UITextView 偶尔会跳来跳去
  3. 尝试滚动视图时 UITextView 随机滚动跳转

请注意,当键盘显示/隐藏时,我确实调整了整个 UITextView 的大小。

于 2014-03-18T03:24:48.237 回答
6

尝试-textViewDidChangeSelection:从 UITextViewDelegate 实现委托方法,如下所示:

-(void)textViewDidChangeSelection:(UITextView *)textView {
    [textView scrollRangeToVisible:textView.selectedRange];
}
于 2014-01-01T20:13:53.073 回答
2

这是 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 不会为插入的文本设置动画。

于 2014-03-26T12:18:40.290 回答
1

恕我直言,这是 iOS 7 中所有典型 UITextView 滚动/键盘相关问题的最终答案。它干净、易于阅读、易于使用、易于维护且易于重用。

基本技巧: 只需更改 UITextView 的大小,而不是内容插图!

这是一个动手的例子。您理所当然地拥有一个使用 Autolayout 的基于 NIB/Storyboard 的 UIViewController,并且 UITextView 填充了 UIViewController 中的整个根视图。如果不是,您将不得不根据您的需要调整更改 textViewBottomSpaceConstraint 的方式。

如何:


添加这些属性:

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

在 Interface Builder 中连接 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];
}
于 2014-05-09T09:40:55.927 回答
0

如果您使用 StoryBoard,那么如果您将 AutoLayout 保持打开(默认情况下)并且没有为 UITextView 设置顶部/底部约束,也会发生这种行为。检查文件检查器以查看您的自动布局状态是什么...

于 2014-03-11T02:02:15.273 回答
0

这是 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;
                    });
                }
            };
于 2015-02-24T17:12:52.053 回答
0
textView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 10.0, 0.0);

这也将解决您的问题

于 2013-12-19T09:32:44.720 回答
0

此行导致最后一行文本不显示给我:

textView.scrollEnabled = false

尝试删除它,看看会发生什么......

于 2016-02-13T21:01:21.833 回答
-1
   textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

这为我解决了这个问题

于 2013-12-13T06:28:02.630 回答
-2

在您的 .m 中将 ViewDelegate 设置为“self”并在您的 .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;
}
于 2014-01-07T05:16:17.960 回答