14

我有一个包含全屏的视图控制器UITextView。当显示键盘时,我想调整文本视图的大小,使其不会隐藏在键盘下。

这是 iOS 的一种相当标准的方法,如以下问题所述:

出现键盘时如何在iOS上调整UITextView的大小?

但是,在 iOS 7 中,如果用户点击屏幕下半部分的文本视图,当文本视图调整大小时,光标仍会离开屏幕。如果用户点击回车,文本视图只会滚动以使光标进入视图。

4

8 回答 8

25

我阅读了谈论这个话题的文档。我把它翻译成 Swift,它对我来说非常好用。

这用于像 iMessage 这样的整页 UITextView。

我在 XCode 6.2 上使用 iOS 8.2 和 Swift,这是我的代码。只需setupKeyboardNotifications从您viewDidLoad或其他初始化方法中调用它。

func setupKeyboardNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWasShown(aNotification:NSNotification) {
    let info = aNotification.userInfo
    let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue
    let kbSize = infoNSValue.CGRectValue().size
    let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0)
    codeTextView.contentInset = contentInsets
    codeTextView.scrollIndicatorInsets = contentInsets
}

func keyboardWillBeHidden(aNotification:NSNotification) {
    let contentInsets = UIEdgeInsetsZero
    codeTextView.contentInset = contentInsets
    codeTextView.scrollIndicatorInsets = contentInsets
}

此外,如果您在旋转时遇到插入符号在正确位置的问题,请检查方向变化并滚动到正确的位置。

override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
    scrollToCaretInTextView(codeTextView, animated: true)
}

func scrollToCaretInTextView(textView:UITextView, animated:Bool) {
    var rect = textView.caretRectForPosition(textView.selectedTextRange?.end)
    rect.size.height += textView.textContainerInset.bottom
    textView.scrollRectToVisible(rect, animated: animated)
}

斯威夫特 3:

func configureKeyboardNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(aNotification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(aNotification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func keyboardWasShown(aNotification:NSNotification) {
    let info = aNotification.userInfo
    let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as! NSValue
    let kbSize = infoNSValue.cgRectValue.size
    let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0)
    textView.contentInset = contentInsets
    textView.scrollIndicatorInsets = contentInsets
}

func keyboardWillBeHidden(aNotification:NSNotification) {
    let contentInsets = UIEdgeInsets.zero
    textView.contentInset = contentInsets
    textView.scrollIndicatorInsets = contentInsets
}

斯威夫特 4 和 5:

func setupKeyboardNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_ :)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}



@objc func keyboardWillShow(_ notification:NSNotification) {
    let d = notification.userInfo!
    var r = (d[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    r = self.textView.convert(r, from:nil)
    self.textView.contentInset.bottom = r.size.height
    self.textView.verticalScrollIndicatorInsets.bottom = r.size.height

}

@objc func keyboardWillHide(_ notification:NSNotification) {
    let contentInsets = UIEdgeInsets.zero
    self.textView.contentInset = contentInsets
    self.textView.verticalScrollIndicatorInsets = contentInsets
}
于 2015-03-30T12:47:25.487 回答
4

使用自动布局,处理起来要容易得多(前提是您了解自动布局):

无需尝试识别和调整受影响的视图,您只需为所有视图的内容创建一个父框架。然后,如果 kbd 出现,您调整框架的大小,并且如果您正确设置了约束,视图将很好地重新排列其所有子视图。无需为此摆弄许多难以阅读的代码。

事实上,在一个类似的问题中,我找到了这个关于这项技术的优秀教程的链接。

此外,此处使用 textViewDidBeginEditing 而不是 UIKeyboardWillShowNotification 的其他示例有一个大问题:

如果用户连接了外部蓝牙键盘,那么即使没有出现屏幕键盘,控件仍会被向上推。这不好。

所以,总结一下:

  1. 使用自动布局
  2. 使用 UIKeyboardWillShowNotification 通知,而不是 TextEditField 的事件来决定何时调整视图大小。

或者,查看 LeoNatan 的回复。这甚至可能是一个更清洁、更简单的解决方案(我自己还没有尝试过)。

于 2014-03-17T20:43:26.313 回答
3

不要调整文本视图的大小。相反,将contentInsetand scrollIndicatorInsetsbottom 设置为键盘高度。

在这里查看我的答案: https ://stackoverflow.com/a/18585788/983912


编辑

我对您的示例项目进行了以下更改:

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    _caretVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(_scrollCaretToVisible) userInfo:nil repeats:YES];
}

- (void)_scrollCaretToVisible
{
    //This is where the cursor is at.
    CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.end];

    if(CGRectEqualToRect(caretRect, _oldRect))
        return;

    _oldRect = caretRect;

    //This is the visible rect of the textview.
    CGRect visibleRect = self.textView.bounds;
    visibleRect.size.height -= (self.textView.contentInset.top + self.textView.contentInset.bottom);
    visibleRect.origin.y = self.textView.contentOffset.y;

    //We will scroll only if the caret falls outside of the visible rect.
    if(!CGRectContainsRect(visibleRect, caretRect))
    {
        CGPoint newOffset = self.textView.contentOffset;

        newOffset.y = MAX((caretRect.origin.y + caretRect.size.height) - visibleRect.size.height + 5, 0);

        [self.textView setContentOffset:newOffset animated:NO];
    }
}

首先删除了设置旧的插入符号位置,以及禁用的动画。现在似乎运作良好。

于 2013-09-23T21:43:37.700 回答
3

虽然@Divya 给出的答案引导我找到了正确的解决方案(所以我授予了赏金),但这并不是一个非常明确的答案!这里是详细的:

确保屏幕键盘不隐藏文本视图的标准方法是在显示键盘时更新其框架,如本问题所述:

出现键盘时如何在iOS上调整UITextView的大小?

但是,在 iOS 7 中,如果您在UIKeyboardWillShowNotification通知处理程序中更改文本视图框架,则光标将保持在屏幕外,如本问题所述。

此问题的解决方法是更改​​文本视图框架以响应textViewDidBeginEditing委托方法:

@implementation ViewController {
    CGSize _keyboardSize;
    UITextView* textView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    textView = [[UITextView alloc] initWithFrame:CGRectInset(self.view.bounds, 20.0, 20.0)];    textView.delegate = self;
    textView.returnKeyType = UIReturnKeyDone;
    textView.backgroundColor = [UIColor greenColor];
    textView.textColor = [UIColor blackColor];
    [self.view addSubview:textView];


    NSMutableString *textString = [NSMutableString new];
    for (int i=0; i<100; i++) {
        [textString appendString:@"cheese\rpizza\rchips\r"];
    }
    textView.text = textString;

}

- (void)textViewDidBeginEditing:(UITextView *)textView1 {
    CGRect textViewFrame = CGRectInset(self.view.bounds, 20.0, 20.0);
    textViewFrame.size.height -= 216;
    textView.frame = textViewFrame;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    CGRect textViewFrame = CGRectInset(self.view.bounds, 20.0, 20.0);
    textView.frame = textViewFrame;
    [textView endEditing:YES];
    [super touchesBegan:touches withEvent:event];
}

@end

注意:不幸textViewDidBeginEdting的是在通知之前触发UIKeyboardWillShowNotification,因此需要对键盘高度进行硬编码。

于 2013-09-27T13:06:25.213 回答
2

以下对我有用:

.h 文件

@interface ViewController : UIViewController <UITextViewDelegate> {

    UITextView *textView ;

}

@property(nonatomic,strong)IBOutlet UITextView *textView;

@end

.m 文件

@implementation ViewController
@synthesize textView;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 424.0f);
    //UITextView *textView = [[UITextView alloc] initWithFrame:textViewFrame];
    textView.frame = textViewFrame;
    textView.delegate = self;
    textView.returnKeyType = UIReturnKeyDone;
    textView.backgroundColor = [UIColor greenColor];
    textView.textColor = [UIColor blackColor];
    [self.view addSubview:textView];

}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
    NSLog(@"textViewShouldBeginEditing:");
    return YES;
}
- (void)textViewDidBeginEditing:(UITextView *)textView1 {
    NSLog(@"textViewDidBeginEditing:");
   CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 224.0f);

    textView1.frame = textViewFrame;

}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView{
    NSLog(@"textViewShouldEndEditing:");
       return YES;
}
- (void)textViewDidEndEditing:(UITextView *)textView{
    NSLog(@"textViewDidEndEditing:");
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
       return YES;
}

- (void)textViewDidChange:(UITextView *)textView{
    NSLog(@"textViewDidChange:");
}

- (void)textViewDidChangeSelection:(UITextView *)textView{
    NSLog(@"textViewDidChangeSelection:");
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSLog(@"touchesBegan:withEvent:");
    CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 424.0f);

    textView.frame = textViewFrame;
    [self.view endEditing:YES];
    [super touchesBegan:touches withEvent:event];
}
@end
于 2013-09-26T07:56:00.870 回答
0

我已经完成了它和它的工作。

  #define k_KEYBOARD_OFFSET 95.0

-(void)keyboardWillAppear {
    // Move current view up / down with Animation
    if (self.view.frame.origin.y >= 0)
    {
        [self moveViewUp:NO];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self moveViewUp:YES];
    }
}

-(void)keyboardWillDisappear {
    if (self.view.frame.origin.y >= 0)
    {
        [self moveViewUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self moveViewUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    //if ([sender isEqual:_txtPassword])
   // {
        //move the main view up, so the keyboard will not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self moveViewUp:YES];
        }
    //}
}

//Custom method to move the view up/down whenever the keyboard is appeared / disappeared
-(void)moveViewUp:(BOOL)bMovedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.4]; // to slide the view up

    CGRect rect = self.view.frame;
    if (bMovedUp) {
        // 1. move the origin of view up so that the text field will come above the keyboard
        rect.origin.y -= k_KEYBOARD_OFFSET;

        // 2. increase the height of the view to cover up the area behind the keyboard
        rect.size.height += k_KEYBOARD_OFFSET;
    } else {
        // revert to normal state of the view.
        rect.origin.y += k_KEYBOARD_OFFSET;
        rect.size.height -= k_KEYBOARD_OFFSET;
    }

    self.view.frame = rect;

    [UIView commitAnimations];
}

- (void)viewWillAppear:(BOOL)animated
{
    // register keyboard notifications to appear / disappear the keyboard
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillAppear)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

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

- (void)viewWillDisappear:(BOOL)animated
{
    // unregister for keyboard notifications while moving to the other screen.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}
于 2015-06-09T11:11:23.300 回答
0

这是我的解决方案,2015 年 7 月在针对 iOS 7.1 的 Xcode 6.4 上使用 Swift 1.2 - 多种方法的组合。借用Johnston 的键盘处理 Swift 代码。它有点像黑客,但它很简单而且很有效。

我在单个视图中有一个香草 UITextView。

根据Apple 的文档,我不想将它嵌入到 UIScrollView 中。我只想在软件键盘出现时重新调整 UITextView 的大小,并在关闭键盘时调整为原始大小。

这些是基本步骤:

  1. 设置键盘通知
  2. 在“Interface Builder”中设置布局约束(在我的情况下,TextView 到底部边缘)
  3. 在相关代码文件中为此约束创建一个 IBOutlet,以便您可以通过编程方式对其进行调整
  4. 使用键盘通知拦截事件并获取键盘大小
  5. 使用键盘大小以编程方式调整约束 IBOutlet 以重新调整 TextView 的大小。
  6. 关闭键盘时将所有内容放回原处。

所以,进入代码。

我已经通过界面构建​​器中的常用拖放在代码文件的顶部设置了约束出口:@IBOutlet weak var myUITextViewBottomConstraint: NSLayoutConstraint!

我还设置了一个全局变量,我可以在键盘出现之前备份事务状态:var myUITextViewBottomConstraintBackup: CGFloat = 0

实现键盘通知,在 viewDidLoad 或任何其他启动/设置部分调用此函数:

func setupKeyboardNotifications() {

    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil)

    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil)

    }

然后在显示/关闭键盘时会自动调用这两个函数:

func keyboardWasShown(aNotification:NSNotification) {

    let info = aNotification.userInfo
    let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as! NSValue
    let kbSize = infoNSValue.CGRectValue().size

    let newHeight = kbSize.height

    //backup old constraint size
    myUITextViewBottomConstraintOld = myUITextViewBottomConstraint.constant 

    // I subtract 50 because otherwise it leaves a gap between keyboard and text view. I'm sure this could be improved on.
    myUITextViewBottomConstraint.constant = newHeight - 50 

func keyboardWillBeHidden(aNotification:NSNotification) {
    //restore to whatever AutoLayout set it before you messed with it
    myUITextViewBottomConstraint.constant = myUITextViewBottomConstraintOld 

}

该代码有效,但有一个小问题:

  • 它不响应键盘打开/关闭上方的预测文本功能区。即当键盘被调用时它会考虑它的状态,但是如果你在显示键盘时向上或向下滑动它,约束将不会被调整。这是一个需要处理的单独事件。它没有足够的功能让我费心。
于 2015-07-12T13:32:26.577 回答
0

@Johnston 找到了一个很好的解决方案。这是一个UIKeyboardWillChangeFrameNotification正确解释键盘大小变化的变体(即显示/隐藏 QuickType 栏)。它还可以正确处理文本视图嵌入导航控制器的情况(即在contentInset其他情况下不为零)。它也是用 Swift 2 编写的。

override func viewDidLoad() {
    :

    NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification, object: nil, queue: nil) { (notification) -> Void in
        guard let userInfo = notification.userInfo,
            let keyboardFrameEndValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue
            else { return }

        let windowCoordinatesKeyboardFrameEnd = keyboardFrameEndValue.CGRectValue() // window coordinates
        let keyboardFrameEnd = self.view.convertRect(windowCoordinatesKeyboardFrameEnd, fromView: nil) // view coordinates

        var inset = self.textView.contentInset
        inset.bottom = CGRectGetMaxY(self.textView.frame) - CGRectGetMinY(keyboardFrameEnd) // bottom inset is the bottom of textView minus top of keyboard
        self.textView.contentInset = inset
        self.textView.scrollIndicatorInsets = inset
    }
}
于 2015-10-08T23:10:55.683 回答