Как изменить размер UITextView на iOS при появлении клавиатуры? - PullRequest
16 голосов
/ 24 августа 2011

В UITabBarController (на iPhone) вставлено UITextView во вкладку.

  1. Заполните UITextView множеством строк.
  2. Показать клавиатуру для редактирования текста.

Что случилось? Клавиатура скрывает половину UITextView с помощью курсора. Невозможно отредактировать текст в результате.

Как решить проблему для всех мобильных устройств Apple (с разным разрешением экрана)? Большое спасибо за помощь!

Ответы [ 10 ]

33 голосов
/ 25 августа 2011

Наилучшего результата достиг следующий код. Также не забудьте установить цвет фона на UIView и поместить UITextView перед другими элементами управления верхнего экрана (например, UITabBar).

Редактирование текста в конце все еще не идеально. Вы можете попытаться улучшить.

FirstViewController.h:

@interface FirstViewController : UIViewController {
    IBOutlet UIBarButtonItem *buttonDone;
    IBOutlet UITextView *textView;
    UITabBarController* tabBarController; // set from superview in AppDelegate (MainWindow.xib)
}

@property (nonatomic, retain) UITabBarController* tabBarController;

FirstViewController.m:

@synthesize tabBarController;

- (void)viewDidAppear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

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

- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
    NSDictionary* userInfo = [aNotification userInfo];
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardEndFrame;

    [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];

    CGRect newFrame = textView.frame;
    CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
    keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height;
    newFrame.size.height -= keyboardFrame.size.height * (up?1:-1);
    textView.frame = newFrame;

    [UIView commitAnimations];   
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    buttonDone.enabled = true;
    [self moveTextViewForKeyboard:aNotification up:YES]; 
}

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    buttonDone.enabled = false;
    [self moveTextViewForKeyboard:aNotification up:NO]; 
}

P.S. Трудно кодировать для iOS без переполнения стека ...

9 голосов
/ 18 марта 2014

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

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

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

3 голосов
/ 31 декабря 2014

Я столкнулся с несколькими проблемами, пытаясь заставить мой текстовый просмотр правильно прокручиваться и анимироваться для iOS 7 и iOS 8, а также с новой функцией QuickType. Сначала я сосредоточился на анимации вставок вида прокрутки, но поведение было разным в iOS 7 и 8, и я не мог заставить его работать правильно для обоих.

Тогда я понял, что могу упростить вещи, просто сосредоточившись на кадре, и это сработало для меня с гораздо более простым кодом. В итоге:

  • зарегистрируйтесь для UIKeyboardDidChangeFrameNotification (это будет уведомлять, когда QuickType также будет показан / скрыт).
  • выясните, сколько вертикального пространства вам нужно, чтобы изменить рамку вашего текстового представления.
  • анимация изменения размера кадра.

Вот код, иллюстрирующий вышесказанное:

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrameWithNotification:) name:UIKeyboardDidChangeFrameNotification object:nil];
}

- (void)keyboardDidChangeFrameWithNotification:(NSNotification *)notification {
    CGFloat keyboardVerticalIncrease = [self keyboardVerticalIncreaseForNotification:notification];
    [self animateTextViewFrameForVerticalOffset:keyboardVerticalIncrease];
}

- (CGFloat)keyboardVerticalIncreaseForNotification:(NSNotification *)notification {
    CGFloat keyboardBeginY = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].origin.y;
    CGFloat keyboardEndY = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].origin.y;
    CGFloat keyboardVerticalIncrease = keyboardBeginY - keyboardEndY;
    return keyboardVerticalIncrease;
}

- (void)animateTextViewFrameForVerticalOffset:(CGFloat)offset {
    CGFloat constant = self.bottomConstraint.constant;
    CGFloat newConstant = constant + offset;
    self.bottomConstraint.constant = newConstant;
    [self.view layoutIfNeeded];
    [UIView animateWithDuration:0.5 animations:^{
        [self.view layoutIfNeeded];
    }];
}

Краткое примечание по анимации. Я использовал Autolayout, поэтому я решил анимировать NSAutoLayoutConstraint текстового представления, а не непосредственно фрейм. И чтобы сделать это, я вызываю [self.view layoutIfNeeded] перед и внутри блока анимации. Это правильный способ анимации ограничений. Я нашел этот совет здесь .

2 голосов
/ 24 марта 2013

Стоит отметить, что ответ «подтвердила» работает только в том случае, если устройство находится в портретном режиме (а не вверх ногами), в других режимах границы идут неправильно.Я полагаю, что вы могли бы отсортировать это с помощью границ для исправления, но я не смог заставить это работать, поэтому приведенная ниже корректировка сработала для меня:

- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {


NSDictionary* userInfo = [aNotification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;

[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];


[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];

CGRect newFrame = self.view.frame;

if (keyboardEndFrame.size.height >keyboardEndFrame.size.width)
{   //we must be in landscape
    if (keyboardEndFrame.origin.x==0)
    {   //upside down so need to flip origin
        newFrame.origin = CGPointMake(keyboardEndFrame.size.width, 0);
    }

    newFrame.size.width -= keyboardEndFrame.size.width * (up?1:-1);

} else
{   //in portrait
    if (keyboardEndFrame.origin.y==0)
    {
        //upside down so need to flip origin
        newFrame.origin = CGPointMake(0, keyboardEndFrame.size.height);
    }
    newFrame.size.height -= keyboardEndFrame.size.height * (up?1:-1);

}
self.view.frame = newFrame;

[UIView commitAnimations];



}
1 голос
/ 14 февраля 2016

Прошли годы, вопрос все еще актуален.Apple определенно должна справиться со всеми этими вещами сама.Но это не так.Вот новое решение, основанное на официальной документации Apple плюс исправления ошибок.Он поддерживает iOS 8, iOS 9, inputAccessoryView и готов к новым версиям iOS и новым устройствам.

/* Apple's solution to resize keyboard but with accessory view support */

- (void)keyboardDidShow:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
    editor.contentInset = contentInsets;
    editor.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardWillHide:(NSNotification*)aNotification {
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    editor.contentInset = contentInsets;
    editor.scrollIndicatorInsets = contentInsets;

    // button to hide the keyboard
    buttonDone.enabled = false;
}

/* Fix issues with size classes and accessory view */

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    // fix incorrect size of the inputAccessoryView when size class changed
    // willTransitionToTraitCollection and traitCollectionDidChange can't help us
    if (editor && editor.inputAccessoryView && !editor.inputAccessoryView.hidden) {
        [editor resignFirstResponder];
    }
}

/* Hide accessory view if a hardware keyboard is present */

#define gThresholdForHardwareKeyboardToolbar 160.f // it's minimum height of the software keyboard on iPhone 4 in landscape mode

- (bool)isExternalKeyboard:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.origin.y;

    return keyboardHeight < gThresholdForHardwareKeyboardToolbar;
}

- (void)keyboardWillShow:(NSNotification*)aNotification {
    if ([self isExternalKeyboard:aNotification]) {
        // hardware keyboard is present
        if (editor && editor.inputAccessoryView) {
            editor.inputAccessoryView.hidden = true;
        }
    } else {
        // only on-screen keyboard
        if (editor && editor.inputAccessoryView) {
            editor.inputAccessoryView.hidden = false;
        }
    }

    // button to hide the keyboard
    buttonDone.enabled = true;
}
1 голос
/ 23 августа 2012
- (void)registerKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(keyboardWillShow:)
     name:UIKeyboardDidShowNotification
     object:nil];

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

- (void)unregisterKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

-(void) keyboardWillHide:(NSNotification *)note
{
   //adjust frame
}

-(void) keyboardWillShow:(NSNotification *)note
{
   //adjust frame 
}

и отмените уведомление тоже в dealloc

- (void)unregisterKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
1 голос
/ 24 августа 2011

Сначала добавьте несколько методов клавиатуры в NSNotificationCenter defaultCenter

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

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                             name:UIKeyboardWillHideNotification object:self.view.window]; 

тогда вы можете изменить размеры:

- (void)keyboardWillShow:(NSNotification *)notif
{
[thetextView setFrame:CGRectMake(20, 49, 280, 187)]; //Or where ever you want the view to go


}

- (void)keyboardWillHide:(NSNotification *)notif
{
[thetextView setFrame:CGRectMake(20, 49, 280, 324)]; //return it to its original position

}
0 голосов
/ 23 мая 2014

Я попробовал лучший ответ здесь, но я нашел в нем проблему. Если у вас есть другое текстовое поле на той же странице, вы щелкаете текстовое поле, показываете клавиатуру. Вы заметите, что текстовое представление сокращается. Однако, если вы щелкнете текстовое представление сейчас, вы заметите, что размер текстового представления снова уменьшается, а это не должно.

Мое решение этой проблемы - сохранить свойство в контроллере представления, представляющее состояние клавиатуры (показано / скрыто). Если клавиатура видна в данный момент, текстовое представление не должно быть уменьшено. Если для ввода текста вы используете клавиатуры разных размеров, вам также следует сохранить старый размер клавиатуры.

Имейте в виду, что это решение также не учитывало другую ориентацию, что может повлиять на способ расчета размера представления текста.

@implementation MyViewController {
    BOOL keyboardShown;
    NSInteger keyboardHeight;
}

- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{
    NSDictionary* userInfo = [aNotification userInfo];
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardEndFrame;

    [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];

    CGRect newFrame = self.textView.frame;
    CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];

    NSInteger oldHeight = self->keyboardShown ? self->keyboardHeight : 0;
    NSInteger newHeight = up ? keyboardFrame.size.height : 0;
    NSInteger change = oldHeight - newHeight;

    self->keyboardShown = up;
    self->keyboardHeight = keyboardFrame.size.height;

    newFrame.size.height += change;
    self.textView.frame = newFrame;

    [UIView commitAnimations];
}
0 голосов
/ 27 сентября 2013

В качестве продолжения методика обновления фрейма при получении уведомления от клавиатуры не работает для iOS 7. Альтернативное решение см. В следующем документе:

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

0 голосов
/ 24 августа 2011

Короче говоря, зарегистрируйте уведомление клавиатуры и сделайте свою работу по изменению размера, когда вы получите уведомление.

...