iOS 7.1 UITextView在新行之后仍然没有滚动到光标/插入符号

时间:2014-03-11 03:16:24

标签: ios ios7 uitextview ios7.1

从iOS 7开始,当用户键入流向新行的文本时,UITextView不会自动滚动到光标。这个问题在SO和其他地方都有详细记载。对我来说,这个问题仍然存在于iOS 7.1中。我做错了什么?

enter image description here

我安装了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中的第一个响应者。滚动到光标所在文本视图的底部。

enter image description here

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

enter image description here

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

enter image description here

5 个答案:

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

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

答案 1 :(得分:9)

在以下情况下,强大的解决方案应该成立:

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

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

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

(4.)复制并粘贴文本

(5。)首次点按返回键创建的新行(请参阅OP中的3个步骤)

(6。)设备旋转

(7。)有些情况我想不到你会...

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

在调用文本视图委托方法 textViewDidChange:时,通常会看到手动滚动到插入符的解决方案。但是,我发现这种技术不能满足上面的情况#5。即使在滚动到插入符号之前调用layoutIfNeeded也无济于事。相反,我必须滚动到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];
    }
}

为什么这样做?我不知道。你将不得不问一位Apple工程师。

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

#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

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

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

如果您只需在用户按下return / enter后进行定位,请尝试:

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

让我知道它是否适合你!

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

答案 4 :(得分:0)

有人制作了一个子类来解决UITextView中所有与滚动相关的问题。实施起来不容易 - 用子类UITextView切换PSPDFTextView

关于它的帖子,显示修复的内容(使用精美的Gif动画)在这里:Fixing UITextView on iOS 7

git在这里:PSPDFTextView