it-roy-ru.com

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

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

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

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

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

16
Dmitry

Прошли годы, вопрос все еще актуален. 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
Dmitry

Наилучшего результата достиг следующий код. Также не забудьте установить цвет фона на 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]; 
}

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

33
Dmitry

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

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

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

9
Thomas Tempelmann

Я столкнулся с несколькими проблемами, пытаясь заставить мой текстовый вид прокручиваться и правильно анимировать как для 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] перед и внутри блока анимации. Это правильный способ анимации ограничений. Я нашел этот совет здесь .

3
guptron

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

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



}
2
Richard Williamson

Сначала добавьте несколько методов клавиатуры в 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

}
1
Matt S.
- (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 
}

и отмените регистрацию уведомлений тоже в раскладе 

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

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

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

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

@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
Harper

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

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

0
ColinE

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

0
Di Wu