iOS7中的UITextView剪辑文本字符串的最后一行

时间:2013-09-23 18:52:49

标签: uitextview ios7

iOS7中的UITextView非常奇怪。当您键入并输入UITextView的最后一行时,滚动视图不会像应该的那样滚动到底部,这会导致文本被“剪切”。我已经尝试将它的clipsToBound属性设置为NO但它仍然剪辑文本。

我不想调用“setContentOffset:animated”,因为对于一个:这是非常hacky的解决方案..其次:如果光标在我们的textview的中间(垂直),它将导致不必要的滚动。 / p>

这是截图。

enter image description here

非常感谢任何帮助!

谢谢!

11 个答案:

答案 0 :(得分: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];
        }];
    }
}

答案 1 :(得分:21)

我发现here的解决方案是在创建UITextView后添加一行修复:

self.textview.layoutManager.allowsNonContiguousLayout = NO;

这一行固定three issues我创建了一个基于UITextView的代码编辑器,在iOS7上使用语法高亮显示:

  1. 滚动以在编辑时将文字保留在视图中(此帖子的问题)
  2. UITextView在解除键盘后偶尔会跳转
  3. 尝试滚动视图时,UITextView随机滚动跳转
  4. 注意,当键盘显示/隐藏时,我确实调整了整个UITextView的大小。

答案 2 :(得分:6)

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

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

答案 3 :(得分: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将无法动画到正在插入的文字。

答案 4 :(得分:1)

Imho这是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];
}

答案 5 :(得分:0)

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

这也将解决您的问题

答案 6 :(得分:0)

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

答案 7 :(得分: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;
                    });
                }
            };

答案 8 :(得分:0)

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

textView.scrollEnabled = false

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

答案 9 :(得分:-1)

   textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

这解决了我的问题

答案 10 :(得分:-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;
}