【问题标题】:UITextView in iOS7 clips the last line of text stringiOS7中的UITextView剪辑最后一行文本字符串
【发布时间】:2013-09-28 18:54:10
【问题描述】:

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

我不想调用“setContentOffset:animated”,因为一个原因:这是非常hacky的解决方案。其次:如果光标位于我们文本视图的中间(垂直),它会导致不必要的滚动。

这是截图。

任何帮助将不胜感激!

谢谢!

【问题讨论】:

  • 我在设备上也遇到了同样的问题。您是否找到任何解决此问题的方法?谢谢!
  • 可能重复。我的答案在这里:stackoverflow.com/a/19200023/4397
  • 抱歉,回复晚了——我放弃了寻找解决方案,并最终使用 textarea 元素构建了我的“文本视图”和嵌入式 Web 视图。不是我理想的解决方案,但它有效:T.
  • 对我来说,iOS 7.0.3 在模拟器和设备上的问题仍然存在。

标签: uitextview ios7


【解决方案1】:

问题是由于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];
        }];
    }
}

【讨论】:

  • 终于找到了一个可行的解决方案!我在这个问题上花了几个小时直到找到这个解决方案......非常感谢!顺便说一句,知道这是否是一个错误吗?我在以前的 iOS 版本上没有这个问题。
  • 非常感谢!我希望它会在未来的 iOS 版本中得到修复。
  • 伟大的工作人 @ davidisdk
  • 当最后一行为空时,它在 iOS 7.1 上不起作用。但是当它不为空时,根本就没有问题。
  • @Altaveron :我和你一样,在 iOS 7.1 上没有任何工作,直到我发现这个:stackoverflow.com/a/19797795/1511646(希望对您有所帮助;)
【解决方案2】:

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

self.textview.layoutManager.allowsNonContiguousLayout = NO;

这一行修复了three issues 我在iOS7 上创建了一个基于UITextView 的带有语法高亮的代码编辑器:

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

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

【讨论】:

  • 对任何事情都没有帮助。
  • “NO”是docs的默认值
  • 真的很好的答案,这应该是最好的答案:)
  • 除了这个之外没有其他东西对我有用!太感谢了。对它不起作用的人可能还有另一个问题。
  • "NO" 是默认值,但它以某种方式起作用。谢谢。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    这是 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 不会对正在插入的文本进行动画处理。

    【讨论】:

    • 当最后一行为空时,它在 iOS 7.1 上不起作用。但是当它不为空时,根本就没有问题。你有针对 iOS 7.1 的修复吗?
    【解决方案5】:

    恕我直言,这是 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];
    }
    

    【讨论】:

      【解决方案6】:
      textView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 10.0, 0.0);
      

      这也将解决您的问题

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

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

            textView.scrollEnabled = false
            

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

            【讨论】:

              【解决方案10】:
                 textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
              

              这为我解决了问题

              【讨论】:

              • 这不是作者想要达到的。正如@Softlion 提到的,这会禁用自动布局。
              【解决方案11】:

              在您的 .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;
              }
              

              【讨论】:

              • 当代码包含CGRectMake(5,5,5,999999999999999999) 之类的内容时,很难认真对待代码。
              • @SteveMadsen 只是scrollToBottom。我本可以告诉他导入 并改用 DBL_MAX,但我认为这太麻烦了,所以我只发送垃圾邮件 9,它具有相同的效果。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-04-04
              • 2017-03-14
              • 1970-01-01
              • 2014-02-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多