当键盘可见时,如何让UITextView正确滚动

时间:2010-09-29 21:03:15

标签: iphone

我有一个UITextView坐在UIView的顶部,如果我点击它打开它进行编辑,那么键盘会阻挡视图的底部,即使我可以在这个区域写入,我也看不到它。我可以告诉UITextView有不同的滚动区域或解决方案是什么?

9 个答案:

答案 0 :(得分:30)

一个更好的解决方案,特别是针对iOS 7,将调整textview的内容插入属性而不是其框架,这样,键盘将模糊落后的文本,就像在任何其他iOS 7应用程序中一样。您还必须调整滚动指示符以匹配。

扩大林德曼的答案,

- (void)keyboardWasShown:(NSNotification*)notification {
    NSDictionary* info = [notification userInfo];
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
    self.textView.scrollIndicatorInsets = self.textView.contentInset;
}

- (void)keyboardWillBeHidden:(NSNotification*)notification {
    self.textView.contentInset = UIEdgeInsetsZero;
    self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

答案 1 :(得分:14)

一个简单的解决方案是实现UITextViewDelegate方法

- (void)textViewDidBeginEditing:(UITextView *)textView

- (void)textViewDidEndEditing:(UITextView *)textView

当键盘出现时,您可以将UITextView框架缩小,并在键盘消失时再次将其设置为完整尺寸...如下所示:

- (void)textViewDidBeginEditing:(UITextView *)textView {
    self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/1.8);
}

- (void)textViewDidEndEditing:(UITextView *)textView {
    self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}

修改

上面的解决方案很糟糕......不要使用!!!

现在我认为按照键盘大小而不是固定值来调整UITextView的大小更好...因为当选择其他语言或设备旋转时键盘的大小会发生变化。 。当然 - .-

首先,您必须注册UIViewController,以显示您的UITextView以接收键盘通知:

- (void)viewDidLoad {
    [super viewDidLoad];

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

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

然后你必须实现两个方法-keyboardWasShown:-keyboardWillBeHidden:

实际键盘的大小包含在NSNotification对象中。

- (void)keyboardWasShown:(NSNotification*)notification {
    NSDictionary* info = [notification userInfo];
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
    self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - keyboardSize.height);
}

- (void)keyboardWillBeHidden:(NSNotification*)notification {
    self.textView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}

答案 2 :(得分:5)

如果您想要消息应用程序样式输入,可以使用嵌套的UITextView(允许多行文本)。它最终会是这样的:

enter image description here

首先布置一个视图来保存所有子视图。这里bottomView的背景颜色设置为与UIKeyboardAppearanceDark匹配。它位于屏幕的底部。

bottomView = [UIView new];
bottomView.frame = CGRectMake(0, h-45, w, 45);
bottomView.backgroundColor = [UIColor colorWithRed:0.078 green:0.078 blue:0.078 alpha:1];
[self.view addSubview:bottomView];

然后,添加一个简单的背景视图,其样式类似于典型的UITextField,并将UITextView作为子视图添加到其中。 inputTV(UITextView)根据字体大小获取其高度。此外,使用textContainer变量从inputTV中删除所有填充。

inputTVBG = [UIImageView new];
inputTVBG.frame = CGRectMake(10, 8, w-90, 29);
inputTVBG.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.1f];
inputTVBG.layer.cornerRadius = 4.0f;
inputTVBG.userInteractionEnabled = true;
inputTVBG.clipsToBounds = true;
[bottomView addSubview:inputTVBG];

inputTV = [UITextView new];
inputTV.font = [UIFont systemFontOfSize:14.0f];
inputTV.frame = CGRectMake(5, 6, w-100, inputTV.font.lineHeight);
inputTV.backgroundColor = [UIColor clearColor];
inputTV.keyboardAppearance = UIKeyboardAppearanceDark;
inputTV.delegate = self;
inputTV.autocorrectionType = UITextAutocorrectionTypeNo;
inputTV.tintColor = [UIColor whiteColor];
inputTV.textColor = [UIColor whiteColor];
inputTV.textContainer.lineFragmentPadding = 0;
inputTV.textContainerInset = UIEdgeInsetsZero;
[inputTVBG addSubview:inputTV];

在上面的示例中,我提供了一个标签,指示剩余的字母数(最大/最小字符数)和提交按钮。

lettersLeftLabel = [UILabel new];
lettersLeftLabel.frame = CGRectMake(w-70, 8, 60, 16);
lettersLeftLabel.font = [UIFont systemFontOfSize:12.0f];
lettersLeftLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5f];
lettersLeftLabel.alpha = 0.0f;
[bottomView addSubview:lettersLeftLabel];

submitButton = [UIButton new];
submitButton.frame = CGRectMake(w-70, 0, 60, 45);
[submitButton setTitle:@"SUBMIT" forState:UIControlStateNormal];
[submitButton setTitleColor:[_peacock.applePink colorWithAlphaComponent:0.5f] forState:UIControlStateNormal];
[submitButton addTarget:self action:@selector(submit) forControlEvents:UIControlEventTouchUpInside];
[submitButton.titleLabel setFont:[UIFont boldSystemFontOfSize:14.0f]];
[bottomView addSubview:submitButton];

在代码的早期添加此行,以便获得键盘更改更新:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

当用户点击inputTV时,它会调用以下方法。在这里它设置变量&keyboard;标注'稍后使用。

-(void)keyboardWillShow:(NSNotification *)n {
    CGRect rect = [n.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect keyboardFrame = [self.view convertRect:rect fromView:nil];
    keyboardHeight = keyboardFrame.size.height;
    [self textViewDidChange:inputTV];
}

这是代码的主要部分,负责处理输入电视的所有移动和调整大小。

-(void)textViewDidChange:(UITextView *)textView {

    //1. letters and submit button vars
    int numberOfCharacters = (int)textView.text.length;
    int minCharacters = 50;
    int maxCharacters = 400;
    int remainingCharacters = maxCharacters-numberOfCharacters;

    //2. if entered letters exceeds maximum, reset text and return
    if (remainingCharacters <= 0){
        textView.text = [textView.text substringToIndex:maxCharacters];
        numberOfCharacters = maxCharacters;
    }

    //3. set height vars
    inputTV.scrollEnabled = true;
    float textHeight = textView.contentSize.height;
    float lineHeight = roundf(textView.font.lineHeight);
    float additionalHeight = textHeight - lineHeight;
    float moveUpHeight = keyboardHeight + additionalHeight;

    //4. default letter colour is weak white
    UIColor * letterColour = [[UIColor whiteColor] colorWithAlphaComponent:0.5f];
    if (numberOfCharacters < minCharacters){ //minimum threshold not met
        lettersLeftLabel.text = [NSString stringWithFormat:@"%i",  minCharacters-numberOfCharacters];
        letterColour = [_peacock.applePink colorWithAlphaComponent:0.5f];
    } else { //within range
        lettersLeftLabel.text = [NSString stringWithFormat:@"%i/%i", numberOfCharacters, maxCharacters];
        if (remainingCharacters<5){ //increase alpha towards the end of range
            letterColour = [[UIColor whiteColor] colorWithAlphaComponent:1.0f - ((float)remainingCharacters/10)];
        }
    }

    //5. hide/show letter label based on textView height
    float letterAlpha = 0.0f; //default hide
    if (additionalHeight > 0){ letterAlpha = 1.0f; } //if multiline, show
    [UIView animateWithDuration:0.3f
                          delay:0.0f
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         lettersLeftLabel.alpha = letterAlpha;
                         lettersLeftLabel.textColor = letterColour;
                     }
                     completion:^(BOOL finished){
                     }];

    //6. update submit colour based on minimum threshold
    UIColor * submitColour = [_peacock.applePink colorWithAlphaComponent:0.5f];
    bool enableSubmit = false;
    if (numberOfCharacters >= minCharacters){
        submitColour = _peacock.applePink;
        enableSubmit = true;
    }
    [submitButton setEnabled:enableSubmit];
    [UIView animateWithDuration:0.3f
                          delay:0.0f
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         [submitButton setTitleColor:submitColour forState:UIControlStateNormal];
                     }
                     completion:^(BOOL finished){
                     }];



    //7. special case if you want to limit the frame size of the input TV to a specific number of lines
    bool shouldEnableScroll = false;
    int maxNumberOfLines = 5; //anything above this triggers the input TV to stay stationary and update its scroll
    int actualNumberOfLines = textHeight / textView.font.lineHeight;
    if (actualNumberOfLines >= maxNumberOfLines){ //recalculate vars for frames
        textHeight = maxNumberOfLines * lineHeight;
        additionalHeight = textHeight - lineHeight;
        moveUpHeight = keyboardHeight + additionalHeight;
        shouldEnableScroll = true;
    }

    //8. adjust frames of views
    inputTV.frame = CGRectMake(5, 6, w-100, textHeight); //update immediately (parent view clips to bounds)
    [UIView animateWithDuration:0.3f
                          delay:0.0f
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         bottomView.frame = CGRectMake(0, h-45-moveUpHeight, w, 45+additionalHeight);
                         inputTVBG.frame = CGRectMake(10, 8, w-90, lineHeight+additionalHeight+13);
                         submitButton.frame = CGRectMake(w-70, additionalHeight, 60, 45);
                     }
                     completion:^(BOOL finished){
                         inputTV.scrollEnabled = shouldEnableScroll; //default disable scroll here to avoid bouncing

                     }];


}

在上述方法中,这就是发生的事情:

  1. 如果要设置最小或最大字符数,可以在此处执行此操作。您拉出字符数并存储为整数,并计算剩余的字符数。

  2. 如果用户已达到最大字符数,请通过剥离回到最大值来重置textView文本。

  3. 这些变量用于计算移动bottomView以及调整其子视图所需的量。

  4. 此方法仅用于更改某些UI元素的颜色/文本。这不是绝对必要的。

  5. 如果你正在使用它,这个方法会将lettersLeftLabel带到视图上。它也没有必要。

  6. 仅在达到最小字符数时才启用提交按钮。它将颜色更改为用户的指示。

  7. 如果要限制inputTV和周围元素的增长,可以包含这段代码。它要求您设置要显示的最大行数。如果用户超过最大值,则为inputTV重新启用滚动,否则默认为false(重要的是停止弹跳)。

  8. 这是主要的调整大小逻辑,将bottomView向上移动并调整其子视图的大小。提交按钮需要保持在相同的位置,因此随着bottomView的增长将其向下移动。

  9. 注意:如果您只想要准系统代码,则只需执行步骤3和8.

答案 3 :(得分:4)

Apple有一些code samples处理这种情况。

答案 4 :(得分:4)

我终于开始工作了。这是我的解决方案,你们可以发现我设计中的任何错误吗?

@synthesize textView = _textView;
@synthesize callbackViewController = _callbackViewController;


-(void)keyboardWasShown:(NSNotification*)aNotification {
    if(keyboardShown) {
        return;
    }

    NSDictionary *info = [aNotification userInfo];

    // Get the size of the keyboard.
    NSValue *aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
    keyboardSize = [aValue CGRectValue].size;

    // Resize the scroll view (which is the root view of the window)
    CGRect viewFrame = [self.textView frame];

    orientationAtShown = orientation;

    if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
        viewFrame.size.height -= keyboardSize.height;
    } else {
        viewFrame.size.height -= keyboardSize.width;
    }

    self.textView.frame = viewFrame;

    // Scroll the active text field into view.
    //CGRect textFieldRect = [activeField frame];
    [self.textView scrollRectToVisible:viewFrame animated:YES];

    keyboardShown = YES;
}

-(void)keyboardWasHidden:(NSNotification*)aNotification {
    if(!keyboardShown) {
        return;
    }

    // Reset the height of the scroll view to its original value
    CGRect viewFrame = [self.textView frame];
    if(orientationAtShown == UIInterfaceOrientationPortrait || orientationAtShown == UIInterfaceOrientationPortraitUpsideDown) {
        viewFrame.size.height += keyboardSize.height;
    } else {
        viewFrame.size.height += keyboardSize.width;
    }

    self.textView.frame = viewFrame;

    keyboardShown = NO;
}

-(void)registerForKeyboardNotifications {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];

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

-(void)viewWillAppear:(BOOL)animated {
    keyboardShown = NO;
    [self registerForKeyboardNotifications];
}

-(void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    if(keyboardShown) {
        [self keyboardWasHidden:nil];
    }

    orientation = interfaceOrientation;

    CGRect viewFrame = [self.textView frame];
    if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
        if(viewFrame.size.width > viewFrame.size.height) {
            CGRect viewFrameFixed = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.height, viewFrame.size.width);
            self.textView.frame = viewFrameFixed;
        }
    } else {
        if(viewFrame.size.width < viewFrame.size.height) {
            CGRect viewFrameFixed = CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.height, viewFrame.size.width);
            self.textView.frame = viewFrameFixed;
        }
    }


    // Return YES for supported orientations
    return YES;
}

答案 5 :(得分:4)

@Alejandro上面有正确的想法,但他的代码在横向模式下不起作用。我修改了他的keyboardWasShown:方法,以便在所有方向上正确运行:

- (void)keyboardWasShown:(NSNotification *)notification {
    if (self.textView != nil) {
        NSDictionary* info = [notification userInfo];
        CGRect keyboardRect = [self.textView convertRect:[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
        CGSize keyboardSize = keyboardRect.size;        

        self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
        self.textView.scrollIndicatorInsets = self.textView.contentInset;
    }
}

答案 6 :(得分:2)

首先在viewDidLoad中添加观察者。

   - (void)viewDidLoad {
    [super viewDidLoad];

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

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];

}

调用方法

- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.textView.contentInset = contentInsets;
self.textView.scrollIndicatorInsets = contentInsets;

// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.textView.frame.origin) ) {
    [self.textView scrollRectToVisible:self.textView.frame animated:YES];
}
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    self.textView.contentInset = contentInsets;
    self.textView.scrollIndicatorInsets = contentInsets;
}

答案 7 :(得分:1)

如果您有超过1个文本字段,或者想要减少代码,请尝试使用此代码

- (void)textFieldDidBeginEditing:(UITextField *)textField{

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.35f];
    CGRect frame = self.view.frame;
    frame.origin.y = (self.view.frame.size.height - textField.frame.origin.y) - self.view.frame.size.height+60;
    if (frame.origin.y<-162) {
        frame.origin.y = -162;
    }
    [self.view setFrame:frame];
    [UIView commitAnimations];
}
-(BOOL)textFieldShouldEndEditing:(UITextField *)textField{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.35f];
    CGRect frame = self.view.frame;
    frame.origin.y = 0;
    [self.view setFrame:frame];
    [UIView commitAnimations];
    return YES;

}

答案 8 :(得分:0)

扩展@alejandro&amp; @Mani:

最终答案:

- (void)keyboardWasShown:(NSNotification *)notification {
    if (self.textView != nil) {
        NSDictionary* info = [notification userInfo];
        CGRect keyboardRect = [self.textNote convertRect:[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
        CGSize keyboardSize = keyboardRect.size;

        self.textView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0);
        self.textView.scrollIndicatorInsets = self.textView.contentInset;
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)notification {

     self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
    self.textView.scrollIndicatorInsets = UIEdgeInsetsZero;
}