|
Май
18
|
Для вывода сообщения “Sending…” над клавиатурой приложение “Text” (SMS) на iPhone использует отдельное полупрозрачное представление. Я расскажу о простом классе, который может выводить полупрозрачные сообщения о загрузке, и научу располагать эти сообщения над клавиатурой.
Сообщения “Loading…”
Процесс загрузки данных из Интернета в большинстве приложений iPhone сопровождается почти черным полупрозрачным интерфейсом, заслоняющим экран. Дополнительные элементы — простейший “счетчик” (UIActivityIndicatorView), подтверждающий, что приложение активно, а также текст “Loading…“.
Несмотря на то, что это сообщение используется практически повсеместно, оно не относится к стандартным элементам управления и создается вручную.
Поиск клавиатуры.
Apple не предлагает методов обнаружения клавиатуры и даже текущего “First Responder” для приложения iPhone. Я покажу, как это делается.
Пробное приложение.
Рассматриваемое в данной статье тестовое приложение “LoadingView” предлагает два типа окон загрузки:
Полноэкранное сообщение о загрузке и сообщение в форме клавиатуры.
На самом деле оно ничего не загружает. Кнопка “Refresh” выводит полноэкранное сообщение о загрузке на 5 сек. Добавив в поле текст и нажав кнопку “Send“, можно в течение такого же промежутка времени понаблюдать за сообщением о загрузке по форме клавиатуры.
Отображение интерфейса загрузки.
Интерфейс загрузки — не самое сложное из пользовательских представлений, но он подразумевает массу внедряемых общих характеристик. Следовательно, для этой цели больше подойдет возвратный (reusable) класс.
Характеристики моего интерфейса загрузки перечислены ниже.
- Всегда заполнять весь блокируемый интерфейс (поля со всех сторон оставлены из эстетических соображений).
- При добавлении и удалении плавно появляться и исчезать.
- Интерфейс полупрозрачен, позволяет видеть сквозь него незагруженное представление.
- Он автоматически подстраивает размеры под окно: смена при загрузке книжной ориентации на альбомную не приведет к искажениям.
- В качестве индикатора процесса используется расположенное по центру сообщение о статусе.
Чтобы убедиться, что все эти характеристики будут применены к создаваемому интерфейсу, вместо обычного конструктора воспользуемся методом loadingViewInView:. Данный метод выполняет сборку, добавляет ее к superview и отвечает за анимацию с исчезновением/выводом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | + (id)loadingViewInView:(UIView *)aSuperview { LoadingView *loadingView = [[[LoadingView alloc] initWithFrame:[aSuperview bounds]] autorelease]; if (!loadingView) { return nil; } loadingView.opaque = NO; loadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [aSuperview addSubview:loadingView]; // Здесь находится код для создания и настройки метки и activity view. // Чтобы увидеть его, загрузите тестовую программу. // Настройка постепенного появления интерфейса. CATransition *animation = [CATransition animation]; [animation setType:kCATransitionFade]; [[aSuperview layer] addAnimation:animation forKey:@"layerAnimation"]; return loadingView; } |
Сделать интерфейс полупрозрачным поможет особый метод рисования.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | - (void)drawRect:(CGRect)rect { rect.size.height -= 1; rect.size.width -= 1; const CGFloat RECT_PADDING = 8.0; rect = CGRectInset(rect, RECT_PADDING, RECT_PADDING); const CGFloat ROUND_RECT_CORNER_RADIUS = 5.0; CGPathRef roundRectPath = NewPathWithRoundRect(rect, ROUND_RECT_CORNER_RADIUS); CGContextRef context = UIGraphicsGetCurrentContext(); const CGFloat BACKGROUND_OPACITY = 0.85; CGContextSetRGBFillColor(context, 0, 0, 0, BACKGROUND_OPACITY); CGContextAddPath(context, roundRectPath); CGContextFillPath(context); const CGFloat STROKE_OPACITY = 0.25; CGContextSetRGBStrokeColor(context, 1, 1, 1, STROKE_OPACITY); CGContextAddPath(context, roundRectPath); CGContextStrokePath(context); CGPathRelease(roundRectPath); } |
Закругленные углы везде!
И все же мне кажется странным, что Apple не предусмотрела среди рисуемых автофигур прямоугольник со закругленными углами. Есть более универсальная функция “CGPathAddArcToPoint“, но ей не хватает актуального для простых случаев рисования сплошной линией. Те, кто не представляет, как нарисовать iPhone закругленный прямоугольник, могут загрузить проект и посмотреть, как реализация “NewPathWithRoundRect” создает такие объекты с помощью функции “CGPathAddArcToPoint“.
Отсутствие такой функции представляется особенно загадочным, если вспомнить анекдот, упомянутый Энди Хертцфельдом (Andy Hertzfeld) на сайте folklore.org и в книге Revolution in the Valley. В нем Стив Джобс вытаскивает Билла Аткинсона на прогулку и показывает ему бесчисленные скругленные прямоугольники вокруг до тех пор, пока Билл не сдается и не соглашается добавить в Quickdraw функцию “RoundRect“.
Скругленные прямоугольники в iPhone на каждом шагу — их, думаю, здесь даже больше, чем на Маках в 1984 г. Использовать такую функцию, как “NewPathWithRoundRect“, в собственном коде — неплохая идея.
Поиск клавиатуры.
Клавиатура в iPhone — экземпляр частного класса “UIKeyboard” в собственном окне “UIWindow” (окно она может делить с “UIAutoCorrectInlineView“).
Найти клавиатуру поможет простой запрос:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @implementation UIApplication (KeyboardView) - (UIView *)keyboardView { NSArray *windows = [self windows]; for (UIWindow *window in [windows reverseObjectEnumerator]) { for (UIView *view in [window subviews]) { if (!strcmp(object_getClassName(view), "UIKeyboard")) { return view; } } } return nil; } @end |
Собственно клавиатура представляет собой серию вложенных представлений, вплоть до функций клавиш нижнего уровня.
1 2 3 4 5 6 7 | <li>UIKeyboard</li> <li>UIKeyboardImpl</li> <li>UIKeyboardLayoutQWERTY</li> <li>UIKeyboardSublayout</li> <li>UIImageView</li> <li>UIKeyboardSpaceKeyView</li> <li>UIKeyboardReturnKeyView</li> |
Интересно, что клавиши основной части клавиатуры являются единым изображением. Это одно из объяснений тому, почему клавиши пробела и возврата по поведению больше напоминают обычные кнопки, нежели остальные клавиши.
Поскольку в тестовом приложении реализация “LoadingView” регулирует размер загружаемого интерфейса, чтобы тот перекрывал superview, предложенный способ передачи интерфейса клавиатуры даст представление загрузки по ее размеру.
Чтобы отобразить перекрывающий клавиатуру полнооконный “LoadingView“, передайте “superview” для интерфейса клавиатуры методу “loadingViewInView:“. В этом случае будет целесообразным добавить сверху поле в 20 пикселей, поскольку окно “superview” в верхней части экрана заходит за панель состояния.
Поиск “firstResponder”
Для тестового проекта этот момент необязателен, но я решил остановиться еще на одном способе получения важного источника информации: “firstResponder” в приложении iPhone.
В iPhone метод “firstResponder” — представление с текущим фокусом клавиатуры (или со значением “nil“, если фокус отсутствует).
Это важная информация, но по какой-то причине Apple решила не предоставлять широкой публике метода для доступа к ней. На самом деле, возвращающий данную величину метод есть. Это “firstResponder” для “UIWindow“, просто немногие о нем знают.
1 2 3 4 5 6 7 | UIView *firstResponder = [[UIApplication sharedApplication] keyWindow] performSelector:@selector(firstResponder)]; |
Заключение.
Вывод загрузочного интерфейса — не слишком сложная задача (многие пишут для этого собственный код), но внедрение всех предполагаемых характеристик отнимает массу времени: для нашего приложения это минимум 65 строк кода. Многоразовая реализация в состоянии это время сэкономить.
Проработку поиска клавиатуры и текущего объекта “First Responder” в iPhone выполнить сложнее, поскольку интерфейс прикладного программирования скрыт — придется проявить таланты исследователя.
Осталось собрать все воедино, и вы легко воссоздадите интерфейс прогресса “Sending…” как в программе Text.


(4 голосов, средний: 4.75 из 5)
Сентябрь 13th, 2011 at 10:14
Здравствуйте.
А поверх MapView такую штуку можно сделать?