25

从 iOS 7开始,UITextView当用户键入换行的文本时,a 不会自动滚动到光标处。这个问题在 SO 和其他地方都有很好的记录。对我来说,这个问题在 iOS 7.1 中仍然存在。我究竟做错了什么?

在此处输入图像描述

我安装了 Xcode 5.1 并针对 iOS 7.1。我正在使用自动布局。

以下是我如何将文本视图的内容定位在键盘上方:

- (void)keyboardUp:(NSNotification *)notification
{
    NSDictionary *info = [notification userInfo];
    CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    keyboardRect = [self.view convertRect:keyboardRect fromView:nil];

    UIEdgeInsets contentInset = self.textView.contentInset;
    contentInset.bottom = keyboardRect.size.height;
    self.textView.contentInset = contentInset;
}

我尝试过的:我已经尝试了许多在此问题上发布到 SO 的解决方案,因为它与 iOS 7 相关。我尝试过的所有解决方案似乎都不适用于显示属性字符串的文本视图在以下三个步骤中,我概述了 SO ( https://stackoverflow.com/a/19277383/1239263 )上投票最多的答案如何响应用户第一次点击返回键。

(1.) 文本视图成为第一个响应者viewDidLoad。滚动到光标所在文本视图的底部。

在此处输入图像描述

(2.) 在输入单个字符之前,点击键盘上的返回键。插入符号消失在视线之外。

在此处输入图像描述

(3.) 然而,再次敲击返回键似乎使情况正常化。(注意:删除后一个新行会使插入符号再次消失)。

在此处输入图像描述

4

5 回答 5

11

改进了UITextView后代类的解决方案代码:

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define is_iOS7 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")
#define is_iOS8 SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")

@implementation MyTextView {
    BOOL settingText;
}

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTextViewDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self];
    }
    return self;
}

- (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated {
    CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end];
    rect.size.height += textView.textContainerInset.bottom;
    [textView scrollRectToVisible:rect animated:animated];
}

- (void)handleTextViewDidChangeNotification:(NSNotification *)notification {
    if (notification.object == self && is_iOS7 && !is_iOS8 && !settingText) {
        UITextView *textView = self;
        if ([textView.text hasSuffix:@"\n"]) {
            [CATransaction setCompletionBlock:^{
                [self scrollToCaretInTextView:textView animated:NO];
            }];
        } else {
            [self scrollToCaretInTextView:textView animated:NO];
        }
    }
}

- (void)setText:(NSString *)text {
    settingText = YES;
    [super setText:text];
    settingText = NO;
}

请注意,在蓝牙键盘上按下 Down 键时它不起作用。

于 2014-06-08T17:41:21.447 回答
9

稳健的解决方案应在以下情况下成立:

(1.) 显示属性字符串的文本视图

(2.) 通过点击键盘上的返回键创建一个新行

(3.) 通过键入溢出到下一行的文本创建的新行

(4.) 复制和粘贴文本

(5.)第一次点击返回键创建一个新行(参见 OP 中的 3 个步骤)

(6.) 设备旋转

(7.) 在某些情况下,我想不出你会...

为了在 iOS 7.1 中满足这些要求,似乎仍然需要手动滚动到插入符号。

当调用文本视图委托方法textViewDidChange:时,通常会看到手动滚动到插入符号的解决方案。但是,我发现这种技术不能满足上面的情况#5。即使在滚动到插入符号之前调用 tolayoutIfNeeded也无济于事。相反,我不得不滚动到CATransaction完成块内的插入符号:

// this seems to satisfy all of the requirements listed above–if you are targeting iOS 7.1
- (void)textViewDidChange:(UITextView *)textView
{
    if ([textView.text hasSuffix:@"\n"]) {

        [CATransaction setCompletionBlock:^{
            [self scrollToCaretInTextView:textView animated:NO];
        }];

    } else {
        [self scrollToCaretInTextView:textView animated:NO];
    }
}

为什么这行得通?我不知道。你得问苹果工程师。

为了完整起见,这是与我的解决方案相关的所有代码:

#import "ViewController.h"

@interface ViewController () <UITextViewDelegate>

@property (weak, nonatomic) IBOutlet UITextView *textView; // full-screen

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSString *string = @"All work and no play makes Jack a dull boy.\n\nAll work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy. All work and no play makes Jack a dull boy.";

    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName: [UIFont fontWithName:@"Verdana" size:30.0]}];

    self.textView.attributedText = attrString;

    self.textView.delegate = self;
    self.textView.backgroundColor = [UIColor yellowColor];
    [self.textView becomeFirstResponder];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardIsUp:) name:UIKeyboardDidShowNotification object:nil];
}

// helper method
- (void)scrollToCaretInTextView:(UITextView *)textView animated:(BOOL)animated
{
    CGRect rect = [textView caretRectForPosition:textView.selectedTextRange.end];
    rect.size.height += textView.textContainerInset.bottom;
    [textView scrollRectToVisible:rect animated:animated];
}

- (void)keyboardIsUp:(NSNotification *)notification
{
    NSDictionary *info = [notification userInfo];
    CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    keyboardRect = [self.view convertRect:keyboardRect fromView:nil];

    UIEdgeInsets inset = self.textView.contentInset;
    inset.bottom = keyboardRect.size.height;
    self.textView.contentInset = inset;
    self.textView.scrollIndicatorInsets = inset;

    [self scrollToCaretInTextView:self.textView animated:YES];
}

- (void)textViewDidChange:(UITextView *)textView
{
    if ([textView.text hasSuffix:@"\n"]) {

        [CATransaction setCompletionBlock:^{
            [self scrollToCaretInTextView:textView animated:NO];
        }];

    } else {
        [self scrollToCaretInTextView:textView animated:NO];
    }
}

@end

如果您发现这不起作用的情况,请告诉我。

于 2014-03-13T05:00:35.727 回答
3

我通过获取插入符号的实际位置并对其进行调整来解决它,这是我的方法:

- (void) alignTextView:(UITextView *)textView withAnimation:(BOOL)shouldAnimate {

    // where the blinky caret is
    CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start];
    CGFloat offscreen = caretRect.origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top);

    CGPoint offsetP = textView.contentOffset;
    offsetP.y += offscreen + 3; // 3 px -- margin puts caret 3 px above bottom

    if (offsetP.y >= 0) {
        if (shouldAnimate) {
            [UIView animateWithDuration:0.2 animations:^{
                [textView setContentOffset:offsetP];
            }];
        }
        else {
            [textView setContentOffset:offsetP];
        }
    }
}

如果您只需要在用户按下回车/回车后进行定向,请尝试:

- (void) textViewDidChange:(UITextView *)textView {
    if ([textView.text hasSuffix:@"\n"]) {
        [self alignTextView:textView withAnimation:NO];
    }
}

请让我知道这对你有没有用!

于 2014-03-11T04:01:06.440 回答
0

我找不到原始来源,但它适用于 iOS7.1

 - (void)textViewDidChangeSelection:(UITextView *)textView
 {
   if ([textView.text characterAtIndex:textView.text.length-1] != ' ') {
        textView.text = [textView.text stringByAppendingString:@" "];
    }

    NSRange range0 = textView.selectedRange;
    NSRange range = range0;
    if (range0.location == textView.text.length) {
        range = NSMakeRange(range0.location - 1, range0.length);
    } else if (range0.length > 0 &&
               range0.location + range0.length == textView.text.length) {
        range = NSMakeRange(range0.location, range0.length - 1);
    }
    if (!NSEqualRanges(range, range0)) {
        textView.selectedRange = range;
    }
 }
于 2014-03-11T13:28:33.620 回答
0

有人制作了一个子类来解决UITextView. 实现再简单不过了——UITextView用子类切换PSPDFTextView

一篇关于它的帖子,显示了修复的内容(带有漂亮的 Gif 动画)在这里:Fixing UITextView on iOS 7

git 在这里:PSPDFTextView

于 2014-04-10T07:52:28.693 回答