Подтвердить что ты не робот

Как изменить размер UITextView, когда клавиатура показана с iOS 7

У меня есть контроллер вида, который содержит полноэкранный UITextView. Когда отображается клавиатура, я хотел бы изменить размер текстового представления, чтобы он не был скрыт под клавиатурой.

Это довольно стандартный подход с iOS, как описано в этом вопросе:

Как изменить размер UITextView на iOS при появлении клавиатуры?

Однако, с iOS 7, если пользователь вступает в текстовое представление в нижней половине экрана, при изменении размера текста курсор остается вне экрана. Текстовое представление только прокручивает, чтобы отобразить курсор, если при входе пользователя нажать.

4b9b3361

Ответ 1

В то время как ответ, данный @Divya, приводит меня к правильному решению (поэтому я наградил щедростью), это не очень ясный ответ! Здесь он подробно:

Стандартный подход к обеспечению того, чтобы текстовое представление не скрывалось экранной клавиатурой, заключается в обновлении его кадра при отображении клавиатуры, как подробно описано в этом вопросе:

Как изменить размер UITextView на iOS при появлении клавиатуры?

Однако с 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, поэтому необходимо жестко закодировать высоту клавиатуры.

Ответ 2

Я прочитал документы, которые говорят об этой теме. Я перевел его в Свифт, и он отлично работал у меня.

Это используется для полного UITextView страницы, такого как iMessage.

Я использую iOS 8.2 и Swift на XCode 6.2, и здесь мой код. Просто позвоните 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)
}

Swift 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
}

Ответ 3

С помощью Auto Layout намного проще (если вы понимаете Auto Layout):

Вместо того, чтобы пытаться идентифицировать и изменять размер затронутых представлений, вы просто создаете родительский фрейм для всего содержимого вашего представления. Затем, если появится kbd, вы измените размер кадра, и если вы правильно настроили ограничения, представление будет хорошо упорядочивать все его дочерние элементы. Не нужно возиться с большим количеством трудночитаемого кода для этого.

Фактически, в подобном вопросе я нашел ссылку на этот отличный учебник об этой технике.

Кроме того, другие примеры здесь, которые используют textViewDidBeginEditing вместо UIKeyboardWillShowNotification, имеют одну большую проблему:

Если у пользователя есть внешняя клавиатура Bluetooth, тогда элемент управления все равно будет нажат, даже если экранная клавиатура не появится. Это нехорошо.

Итак, суммируем:

  • Использовать автоматический макет
  • Используйте уведомление UIKeyboardWillShowNotification, а не события TextEditField для принятия решения о том, когда просмотров.

В качестве альтернативы, проверьте ответ LeoNatan. Это может быть даже более чистым и простым решением (я еще не пробовал себя).

Ответ 4

Не изменять размер текстового представления. Вместо этого установите дно contentInset и scrollIndicatorInsets на высоту клавиатуры.

См. мой ответ здесь: fooobar.com/questions/102966/...


Изменить

В ваш образец проекта были внесены следующие изменения:

- (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];
    }
}

Сначала удалена настройка старой позиции каретки, а также отключена анимация. Теперь, похоже, хорошо работает.

Ответ 5

Следующее работает для меня:

.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

Ответ 6

Я сделал это и его работу полностью.

  #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];
}

Ответ 7

Это мое решение, июль 2015 года, с использованием Swift 1.2 на Xcode 6.4, ориентированного на iOS 7.1 - комбинация нескольких подходов. Заемная клавиатура Johnston, передающая Swift-код. Его немного взломать, но его просто и он работает.

У меня есть UITextView ванили в одном представлении.

Я не хотел встроить его в UIScrollView в соответствии с документацией Apple. Я просто хотел, чтобы UITextView изменился, когда появилась клавиатура программного обеспечения, и изменилась на оригинал, когда клавиатура была уволена.

Это основные шаги:

  • Настройка уведомлений клавиатуры
  • Установите ограничение компоновки в "Interface Builder" (TextView до нижнего края в моем случае)
  • Создайте IBOutlet для этого ограничения в соответствующем файле кода, чтобы вы могли его программно настроить
  • Использовать уведомления о клавиатуре для перехвата событий и получения размера клавиатуры.
  • Программно настроить ограничение IBOutlet с использованием размера клавиатуры для изменения размера TextView.
  • Поместите все обратно, когда клавиатура отклоняется.

Итак, на код.

Я установил выход ограничения в верхней части файла кода с помощью обычного перетаскивания в конструкторе интерфейса: @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 

}

Код работает с незначительной проблемой:

  • Он не реагирует на прогностическую текстовую ленту над клавиатурой, открывая/закрывая. То есть он будет учитывать его состояние при вызове клавиатуры, но если вы должны сдвигать его вверх или вниз, пока отображается клавиатура, ограничение не будет изменено. Это отдельное событие, которое необходимо обработать. Недостаточно функциональности для меня, чтобы беспокоиться.

Ответ 8

@Джонстон нашел хорошее решение. Здесь изменяется вариант с использованием 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
    }
}