Обсуждение

Обсуждение

У устройств, работающих с операционной системой iOS, нет физической клавиатуры. Но у них есть виртуальная клавиатура, всплывающая на экране всякий раз, когда пользователь должен ввести текст в какое-нибудь текстовое поле (UITextField, см. раздел 1.19) или текстовый вид (UITextView, см. раздел 1.20). На iPad пользователь даже может делить клавиатуру на части и перемещать их вверх и вниз. Есть несколько пограничных случаев, о которых, вам, возможно, придется позаботиться при проектировании пользовательского интерфейса. Можете обратиться к помощи дизайнеров пользовательских интерфейсов (если в вашей компании есть такие специалисты) и рассказать им, что на iPad пользователь может делить клавиатуру на части. Перед тем как приступать к творческой работе, они должны об этом узнать. В этом разделе мы коснемся подобного пограничного случая.

Сначала рассмотрим, как выглядит клавиатура в iPhone. Виртуальная клавиатура может отображаться в книжной или альбомной ориентации. В книжной ориентации клавиатура iPhone выглядит так, как на рис. 15.1.

Рис. 15.1. Клавиатура на iPhone, книжная ориентация

А клавиатура в альбомной ориентации на iPhone будет выглядеть так, как на рис. 15.2.

Рис. 15.2. Клавиатура в iPhone, альбомная ориентация

Но на iPad клавиатура выглядит немного иначе. Самое очевидное отличие заключается в том, что эта клавиатура гораздо крупнее, чем на iPhone, поскольку сам экран у iPad шире. При альбомной ориентации клавиатура iPad значительно шире, но на ней содержится тот же набор клавиш, что и при книжной ориентации. Кроме того, пользователь может при желании «разбирать» клавиатуру iPad на части. Благодаря этому он лучше контролирует функционирование клавиатуры, зато появляются дополнительные проблемы у программистов, дизайнеров пользовательских интерфейсов и пользовательских взаимодействий.

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

• UIKeyboardWillShowNotification — распространяется, когда клавиатура вот-вот появится на экране. Уведомление несет с собой словарь с пользовательской информацией, в котором содержатся различные данные о клавиатуре, анимации, которая будет применяться при выводе клавиатуры на экран, и другая информация;

• UIKeyboardDidShowNotification — распространяется, когда клавиатура уже появилась на экране;

• UIKeyboardWillHideNotification — распространяется, когда клавиатура вот-вот будет убрана с экрана. Уведомление несет с собой словарь с пользовательской информацией, в котором содержатся различные данные о клавиатуре, анимации, которая будет применяться при уходе клавиатуры с экрана, длительности анимации и т. д.;

• UIKeyboardDidHideNotification — распространяется после того, как клавиатура полностью скроется с экрана.

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

• UIKeyboardAnimationCurveUserInfoKey — значение этого ключа характеризует тип анимационной кривой, которая будет использоваться при выводе клавиатуры на экран или ее скрытии. Этот ключ содержит значение типа NSNumber (инкапсулированное в объекте типа NSValue), которое, в свою очередь, содержит беззнаковое целое типа NSUInteger;

• UIKeyboardAnimationDurationUserInfoKey — значение данного ключа указывает в секундах длительность анимации, применяемой при отображении или скрытии клавиатуры. Если клавиатура вот-вот будет отображена, то это будет рамка, в которой появится клавиатура. Если клавиатура уже есть на экране, это будет контур, обрамляющий клавиатуру на экране перед тем, как она уйдет с экрана. Этот ключ содержит значение типа CGRect (инкапсулированное в объекте типа NSValue);

• UIKeyboardFrameBeginUserInfoKey — значение данного ключа указывает размеры рамки клавиатуры до того, как начнется анимация. Если клавиатура вот-вот отобразится, то перед появлением клавиатуры появится эта рамка. Если клавиатура в данный момент отображена и вот-вот уйдет с экрана, это будет рамка, фактически занимаемая клавиатурой на экране, прежде чем клавиатура уйдет с экрана в сопровождении соответствующей анимации. Этот ключ содержит значение типа CGRect (инкапсулированное в объект типа NSValue);

• UIKeyboardFrameEndUserInfoKey — значение данного ключа описывает контур клавиатуры, который оформится после того, как будет применена анимация. Если клавиатура вот-вот появится на экране, то она займет именно этот контур. Если клавиатура уходит с экрана, то именно этот контур от нее освободится. Этот ключ содержит значение типа CGRect (инкапсулированное в объекте типа NSValue).

Рассмотрим пример. Мы собираемся создать простое приложение с единственным видом. Это приложение будет работать только на iPhone. В нем будут отображаться вид с изображением и текстовое поле. Текстовое поле находится в нижней части экрана. Итак, когда пользователь дотрагивается до текстового поля, чтобы ввести в него некоторый текст, на экране всплывает виртуальная клавиатура, полностью заслоняющая текстовое поле. Наша задача — анимировать содержимое вида и перераспределить элементы так, чтобы все они оставались видимыми, даже если экран наполовину закрыт клавиатурой. В этом приложении мы будем использовать раскадровки. В контроллере вида заполним вид с изображением, поместив в него прокручивающийся вид, и поместим в этом прокручивающемся виде и вид с изображением, и текстовое поле (рис. 15.3).

Рис. 15.3. Простая раскадровка, содержащая вид с изображением и текстовое поле

Прокручивающийся вид в данном примере является родительским видом как для вида с изображением, так и для текстового поля. Он целиком заполняет пространство своего родительского вида.

Я уже прикрепил прокручивающийся вид, вид с изображением и текстовое поле, определенные в раскадровке, к файлу реализации контроллера вида, вот так:

#import «ViewController.h»

@interface ViewController () <UITextFieldDelegate>

@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;

@property (weak, nonatomic) IBOutlet UITextField *textField;

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

Теперь, когда аутлеты прикреплены к свойствам в контроллере нашего вида, можно приступить к слушанию клавиатурных уведомлений:

— (void) viewWillAppear:(BOOL)paramAnimated{

[super viewWillAppear: paramAnimated];

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

[center addObserver: self selector:@selector(handleKeyboardWillShow:)

name: UIKeyboardWillShowNotification object: nil];

[center addObserver: self selector:@selector(handleKeyboardWillHide:)

name: UIKeyboardWillHideNotification object: nil];

}

— (void)viewWillDisappear:(BOOL)paramAnimated{

[super viewWillDisappear: paramAnimated];

[[NSNotificationCenter defaultCenter] removeObserver: self];

}

Многие программисты допускают распространенную ошибку: продолжают слушать клавиатурные уведомления, когда контроллер вида не отображается на экране. Они начинают слушать уведомления в методе viewDidLoad, а удаляют элементы, действовавшие в качестве наблюдателей, только в методе dealloc. Такой подход проблематичен, так как, когда наш вид не отображается на экране, а клавиатура вновь открывается в каком-то другом виде, вы не должны корректировать положение каких-либо других компонентов контроллера вида. Учтите, что клавиатурные уведомления, как и любые другие, широковещательно передаются всем объектам-наблюдателям в контексте вашего приложения. Поэтому придется принимать дополнительные меры, чтобы программа реагировала не на все клавиатурные уведомления. Если клавиатурные уведомления поступают от вида, не отображаемого на экране в данный момент, они должны игнорироваться.

В предыдущем фрагменте кода мы начали слушать уведомления типа «клавиатура будет отображена» в методе экземпляра handleKeyboardWillShow: контроллера нашего вида. Уведомления типа «клавиатура будет скрыта» мы ожидаем в методе handleKeyboardWillHide:. Пока эти методы еще не написаны. Начнем с первого метода, handleKeyboardWillShow:. В этом методе нам требуется определить высоту клавиатуры, воспользовавшись ключом UIKeyboardFrameEndUserInfoKey из словаря с пользовательской информацией, сопровождающего уведомление. Это значение мы используем, чтобы переместить содержимое вида вверх — так, чтобы все необходимые элементы оказались над клавиатурой. Здесь приятно вспомнить, что мы поместили все нужное содержимое в прокручивающемся виде. Соответственно, потребуется всего лишь откорректировать краевые отступы прокручивающегося вида:

— (void) handleKeyboardWillShow:(NSNotification *)paramNotification{

NSDictionary *userInfo = paramNotification.userInfo;

/* Получаем длительность клавиатурной анимации — время, за которое

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

содержимого вида мы будем применять такое же значение длительности.

*/

NSValue *animationDurationObject =

userInfo[UIKeyboardAnimationDurationUserInfoKey];

NSValue *keyboardEndRectObject = userInfo[UIKeyboardFrameEndUserInfoKey];

double animationDuration = 0.0;

CGRect keyboardEndRect = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f);

[animationDurationObject getValue:&animationDuration];

[keyboardEndRectObject getValue:&keyboardEndRect];

UIWindow *window = [UIApplication sharedApplication].keyWindow;

/* Переводим размеры контура клавиатуры из координатной системы окна

в координатную систему нашего вида. */

keyboardEndRect = [self.view convertRect: keyboardEndRect

fromView: window];

/* Определяем, в какой степени наш вид накрыт клавиатурой */

CGRect intersectionOfKeyboardRectAndWindowRect =

CGRectIntersection(self.view.frame, keyboardEndRect);

/* Прокручиваем прокручивающийся вид таким образом, чтобы содержимое

нашего вида отображалось полностью */

[UIView animateWithDuration: animationDuration animations: ^{

self.scrollView.contentInset =

UIEdgeInsetsMake(0.0f,

0.0f,

intersectionOfKeyboardRectAndWindowRect.size.height,

0.0f);

[self.scrollView scrollRectToVisible: self.textField.frame animated: NO];

}];

}

У нас получился довольно интересный и прямолинейный код. Единственная деталь, возможно требующая дополнительного разъяснения, — это функция CGRectIntersection. В ней мы получаем информацию о прямоугольном контуре клавиатуры (о верхней границе, левой границе, ширине и высоте). Это параметры клавиатуры в момент завершения анимации, когда она полностью отобразится на экране. Теперь, зная параметры клавиатуры, можем воспользоваться функцией CGRectIntersection и определить, какая часть нашего вида накрыта клавиатурой. Итак, берем контур клавиатуры, контур вида, а затем определяем, какая часть контура вида накрыта контуром клавиатуры. В результате получаем структуру типа CGRect, соответствующую той прямоугольной области вида, которая накрыта клавиатурой. Известно, что клавиатура появляется на нижней границе экрана и в ходе анимации выплывает вверх. Поэтому нас интересует вертикаль этой области. Итак, мы получаем высоту области пересечения контура клавиатуры и контура вида, а затем поднимаем на эту высоту содержимое вида. Длительность анимации перемещения задаем равной длительности анимации выдвижения клавиатуры. Таким образом, движения клавиатуры и поднимающихся экранных элементов синхронизируются.

Далее нужно написать метод handleKeyboardWillHide:. В нем мы будем скрывать клавиатуру — соответственно, она больше не будет закрывать наш вид. Итак, в этом методе всего лишь требуется сбросить размеры краевых отступов прокручивающегося вида к начальным значениям, перенести все элементы обратно вниз, чтобы вид выглядел точно так же, как было до появления клавиатуры:

— (void) handleKeyboardWillHide:(NSNotification *)paramSender{

NSDictionary *userInfo = [paramSender userInfo];

NSValue *animationDurationObject =

[userInfo valueForKey: UIKeyboardAnimationDurationUserInfoKey];

double animationDuration = 0.0;

[animationDurationObject getValue:&animationDuration];

[UIView animateWithDuration: animationDuration animations: ^{

self.scrollView.contentInset = UIEdgeInsetsZero;

}];

}

И последний важный момент. Поскольку наш контроллер вида является делегатом текстового поля, необходимо обеспечить уход клавиатуры с экрана, если пользователь нажимает клавишу Return (Ввод) после ввода той или иной информации в текстовое поле:

— (BOOL) textFieldShouldReturn:(UITextField *)paramTextField{

[paramTextField resignFirstResponder];

return YES;

}

Данный текст является ознакомительным фрагментом.