Разработка Всплывающие сообщения для iOS

Никита Narmo Дёнин avatar | 70
FavoriteLoading В закладки
Всплывающие сообщения для iOS

Для отображения всплывающих сообщений в iOS есть только один компонент — UIAlertView. Им очень удобно пользоваться, ведь создать и отобразить его можно буквально двумя-тремя строчками кода. Но у него есть один изъян: он блокирует интерфейс до того момента, пока пользователь не нажмёт кнопку, чтобы принудительно скрыть его. А иногда программисту всего лишь нужно показать какое-либо информационное сообщение, не требующее от пользователя никаких действий. В Android это решено с помощью компонента Toast, в который передаётся текст сообщения (а при необходимости и координаты, в которых оно должно отобразиться).

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

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

Вот задачи, которые я возложил на свой «тостер»:

– отобразить сообщение с анимацией
– подождать 1 секунду
– скрыть сообщение с анимацией

Я сконструировал объект-тостер так, чтобы он создавался и отображался одной строчкой кода:

[WToast showWithText:text];

После вызова этого метода объект сам контролирует своё поведение (появление, ожидание и скрытие). Прежде всего нам надо создать текстовую метку, которая и будет содержать наш текст. Так как текст может быть любой длины, придётся динамически обсчитывать размеры создаваемого компонента UILabel. Для этого в фреймворке UIKit есть специальный метод для объектов NSString, позволяющий узнать размеры текста при заданных шрифте, ширине и типе переноса строк.

Итак, создаём объект UILabel и вычисляем размеры текста:

UILabel *textLabel = [[UILabel alloc] init];
textLabel.backgroundColor = [UIColor clearColor];
textLabel.textAlignment = UITextAlignmentCenter;
textLabel.font = [UIFont systemFontOfSize:14];
textLabel.textColor = RGB(255, 255, 255);
textLabel.numberOfLines = 0;
textLabel.lineBreakMode = UILineBreakModeWordWrap;
CGSize sz = [text sizeWithFont:textLabel.font constrainedToSize:CGSizeMake(width - 20.0f, 9999.0f) lineBreakMode:textLabel.lineBreakMode];

Свойство numberOfLines, выставленное в 0, указывает метке, что количество строк в ней не ограничено. Свойство lineBreakMode сообщает, что при достижении правой границы будет происходить перенос по словам (это нужно, чтобы окончание строки не превратилось в многоточие). В методе -sizeWithFont:constrainedToSize:lineBreakMode: параметр constrainedToSize указывает максимальный размер будущей текстовой метки. Передав туда высоту 9999 пикселей, я гарантировано получу метку без многоточий в конце строки (ведь высота экрана iPhone составляет всего 480 пикселей).

Теперь нужно создать контейнер WToast (который я сделал наследником UIView), добавить ему полупрозрачный фон (так он смотрится красивее, чем если бы был непрозрачным), скруглить углы, поместить на него текстовую метку и отобразить его на экране.

Создаём контейнер:

CGRect tmpRect;
tmpRect.size.width = width;
tmpRect.size.height = MAX(sz.height + 20.0f, 38.0f);
tmpRect.origin.x = floor((screenWidth - width) / 2.0f);
tmpRect.origin.y = floor(screenHeight - tmpRect.size.height - 15.0f);

WToast *toast = [[WToast alloc] initWithFrame:tmpRect];
toast.backgroundColor = RGBA(0, 0, 0, 0.8f);

Обратите внимание на указание координат x и y для метки. Все координаты в iOS представлены типом float, который может принимать дробные значения. Если какому-либо элементу интерфейса задать дробные координаты, он отобразится «замыленным» (кстати, некоторые приложения в App Store грешат этим; даже в приложении от Apple для iTunes Connect была такая проблема). Во избежание этого эффекта нужно округлять координаты до целого значения. Функция floor округлит значение до нижней целой границы — то есть просто отбросит дробную часть.

RGBA — это макрос, который я написал для преобразования параметров RGB и alpha в UIColor. Он делает код компактнее и позволяет работать с RGB в привычном диапазоне 0..255 (а не 0..1, как это сделано в UIColor). Сравните размеры строки с оригинальным вызовом UIColor:

UIColor *blackTransparentColor = RGBA(0, 0, 0, 0.8f);
UIColor *blackTransparentColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f];

Определение макроса можно будет посмотреть в исходном коде компонента, ссылку на него я дам в конце статьи.

Мы получили наследника UIView чёрного цвета и с прозрачностью 0.8. Теперь нужно скруглить ему углы (иначе он будет выглядеть очень топорно). Для скругления можно воспользоваться инструментами, доступными в фреймворке QuartzCore. Подключаем заголовочный файл QuartzCore/QuartzCore.h и фреймворк QuartzCore и делаем следующее:

CALayer *layer = toast.layer;
layer.masksToBounds = YES;
layer.cornerRadius = 5.0f;

Параметр cornerRadius объекта CALayer задаёт радиус скругления. Попробуйте поиграться с его значением и увидите, как будет изменяться рамка нашего уведомления.

Контейнер сообщения готов, текстовую метку мы создали в самом начале — собираем это всё воедино:

textLabel.text = text;
tmpRect.origin.x = floor((toast.frame.size.width - sz.width) / 2.0f);
tmpRect.origin.y = floor((toast.frame.size.height - sz.height) / 2.0f);
tmpRect.size = sz;
textLabel.frame = tmpRect;
[toast addSubview:textLabel];
[textLabel release];
toast.alpha = 0.0f;

После выполнения этого кода наш новорожденный объект toast будет полностью прозрачным (toast.alpha = 0.0f). Это сделано для дальнейшей анимации появления.

Для запуска анимации можно воспользоваться простейшим способом, предусмотренным разработчиками iOS — блоком [UIView beginAnimations:context:] .. [UIView commitAnimations]. Все действия с объектами интерфейса, помещённые в этот блок, будут анимироваться (точнее, не совсем все; ниже я приведу пример, где анимация работать не будет):

[UIView beginAnimations:@"show" context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.2f];
[UIView setAnimationDidStopSelector:@selector(__animationDidStop:__finished:__context:)];
self.alpha = 1.0f;
[UIView commitAnimations];

Здесь мы задаём делегата, которому будет сообщено об окончании процесса анимации, продолжительность анимации и действия, которые нужно анимировать. В нашем случае это перевод объекта toast из полностью прозрачного состояния в полностью непрозрачное (self.alpha = 1.0f).

Вот что мы увидим на экране в результате выполнения нашей программы:

В методе -__animationDidStop:__finished:__context:, который вызовется по окончании анимации, включается секундный таймер, запускающий скрытие нашего сообщения:

[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(__hide) userInfo:nil repeats:NO];

Скрытие сообщения также будет анимированным:

[UIView beginAnimations:@"hide" context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.8f];
[UIView setAnimationDidStopSelector:@selector(__animationDidStop:__finished:__context:)];
self.alpha = 0.0f;
[UIView commitAnimations];

После этого объект можно удалить с экрана, вызвав метод removeFromSuperview.

Если поместить вызов removeFromSuperview в блок анимации, то исчезновение не будет анимироваться, объект исчезнет сразу после вызова этого метода. Поэтому сначала делаем «тостер» полностью прозрачным (self.alpha = 0.0f), а затем, в селекторе окончания анимации, убираем объект с экрана.

Весь цикл работы компонента будет выглядеть так:

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


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

Исходные коды компонента WToast и тестовый проект можно загрузить с github. Этот компонент можно свободно использовать в любом вашем проекте без указания авторства. Он распространяется на условиях Public Domain.

1 Звезд2 Звезды3 Звезды4 Звезды5 Звезд (1 голосов, общий рейтинг: 5.00 из 5)
undefined
iPhones.ru
Для отображения всплывающих сообщений в iOS есть только один компонент — UIAlertView. Им очень удобно пользоваться, ведь создать и отобразить его можно буквально двумя-тремя строчками кода. Но у него есть один изъян: он блокирует интерфейс до того момента, пока пользователь не нажмёт кнопку, чтобы принудительно скрыть его. А иногда программисту всего лишь нужно показать какое-либо...
Прокомментировать

🙈 Комментарии 70

  1. iFanatik avatar
    iFanatik8 апреля 2011
    0

    Спасибо за реально интересную статью. Давно таких не было.

    i.band avatar
    i.band8 апреля 2011
    0

    А вот интересно, разве всплывающие уведомления которые при изменении звука или при блокировке ориентации появляются, например, использовать нельзя для подобной цели?

  2. Vizakenjack avatar
    Vizakenjack8 апреля 2011
    0

    Отличная статья. Стоит на хабре запостить, хотя бы в песочницу

    iL0bster avatar
    iL0bster8 апреля 2011
    0

    @Vizakenjack, на хабр уже не получится: там копипаст даже своего контента запрещён. А вот как топик-ссылку… Кстати, статья великолепна! А то уже устал читать про птиц злобных. Побольше бы таких статей!

  3. Theodore Plesha avatar
    Theodore Plesha8 апреля 2011
    0

    Вообщето разрешение iPhone 4 960×640.

    Никита Narmo Дёнин avatar
    0

    @Theodore Plesha, На программном уровне в iPhone оно 320×480 для любого экрана.

    i.band avatar
    i.band8 апреля 2011
    0

    @Никита Narmo Дёнин, ненене. разрешение будет 960 на 640 пикселей, но 320 на 240 поинтов. Это сделано для удобства разработчиков, т.к. 1 поинт на 4м айфоне будет считаться за квадрат 2 на 2 пикселя, а на 3Гс — за один пиксель.
    Однако есть @property CGFloat contentScaleFactor; которое возвращает «масштаб» (сколько пикселей в поинте для данного view на экране).
    Так что можно использовать и пиксели, но обычно они используются для 3Д объектов, например. А для задания размеров как в нашем примере — поинты :)

    Никита Narmo Дёнин avatar
    0

    @i.band, Я про поинты и говорил как раз.

    i.band avatar
    i.band8 апреля 2011
    0

    @Никита Narmo Дёнин, я понял)) просто решил еще раз расставить все точки над i :)

    PS спасибо за статью. очень нравятся такие статьи.
    PPS там наверху коммент оставил про всплывающие полупрозрачные нотификаторы (при изменении громкости боковыми кнопками и тп). они доступны в API? их можно для подобной цели использовать? :)

    Никита Narmo Дёнин avatar
    0

    @i.band, Нет, к сожалению.

  4. DinckelMan avatar
    DinckelMan8 апреля 2011
    0

    прикольная штукень, в разы лучше и удобней чем текущие окна с сообщениями

  5. TauruS_iPhone4_now avatar
    TauruS_iPhone4_now8 апреля 2011
    0

    Хорошая статья.
    Надеюсь в будущем такие статьи будут появляться еще

    Никита Narmo Дёнин avatar
    0

    @TauruS_iPhone4_now, Я тоже надеюсь :-) Надо придумать, о чём ещё написать.

    iL0bster avatar
    iL0bster8 апреля 2011
    0

    @Никита Narmo Дёнин, идея: Такой твик в сидию :)

    Никита Narmo Дёнин avatar
    0

    @iL0bster, Не, тут я пас.

    iL0bster avatar
    iL0bster8 апреля 2011
    0

    @Никита Narmo Дёнин, а что так? Многие были бы довольны.

    Никита Narmo Дёнин avatar
    0

    @iL0bster, Я на таком низком уровне не пишу. Я больше по прикладным приложениям.

    andreykalol avatar
    andreykalol9 апреля 2011
    0

    @Никита Narmo Дёнин, Не ужели всё так принципиально?
    Я вот бы тоже не отказался от такого твика)

    vetok avatar
    vetok8 апреля 2011
    0

    @Никита Narmo Дёнин, надо придумать как это прикрутить в айос

  6. Yogi avatar
    Yogi8 апреля 2011
    0

    Спасибо за статью! Надеюсь вы дальше тоже будите выкладывать подобные уроки, очень полезно!

  7. zanzy avatar
    zanzy8 апреля 2011
    0

    Говорю спасибо автору, что хоть как-то разбавляет контент интересными статьями. Кстати могу подкинуть пару идей, про что писать дальше =)

    Никита Narmo Дёнин avatar
    0

    @zanzy, Давай :-) Можно на форуме в личку (мой ник — Narmo).

  8. b00bl1chello avatar
    b00bl1chello8 апреля 2011
    0

    Отличная статься! Спасибо! Нужно копию в Apple послать! Пускай пересмотрят свои всплывающие окна!

  9. RoLife avatar
    RoLife8 апреля 2011
    0

    Автору ряльно огромной спасибо. Велекопный твик. Давно таких статей небыло. А главное 1ой строчкой вызов – ммм.

    RoLife avatar
    RoLife8 апреля 2011
    0

    печаль ) залип за клавой, сор за орфографию :)

    Никита Narmo Дёнин avatar
    0

    @RoLife, Спасибо! Я — за лаконичность вызовов :-)

  10. MAXiDROME avatar
    MAXiDROME8 апреля 2011
    0

    Статья хорошая, только зачем она здесь? Для этого ведь есть форум, там и обсуждать удобнее.

    Артур Малосиев avatar
    Артур Малосиев8 апреля 2011
    0

    @MAXiDROME, а чего тут обсуждать? Все и так понятно.

    MAXiDROME avatar
    MAXiDROME9 апреля 2011
    0

    @Артур Малосиев, много приложений под айфон разработал, что все понятно? )))))))

  11. berkutiv avatar
    berkutiv8 апреля 2011
    0

    не подскажите, где можно научится всему этому? очень хочется писать программы под айфон, а я умею только сайты…

    Никита Narmo Дёнин avatar
    0

    @berkutiv, Попробуй начать с раздела для разработчиков здесь, на форуме. И не бойся экспериментировать. Поставь Xcode, почитай документацию от Apple. Практически к каждому разделу их документации имеются примеры.

    berkutiv avatar
    berkutiv8 апреля 2011
    0

    @Никита Narmo Дёнин, аа..спасибо:)

    i.band avatar
    i.band8 апреля 2011
    0

    @berkutiv, и книги можно почитать. сейчас даже выходят русские. плюс iTunes U с лекциями. И google.com :)

  12. Arsenbl4 avatar
    Arsenbl48 апреля 2011
    0

    Простите, что не в тему=(( Сделал джейл 4.2.1 через Redsn0w (привязанный), пытаюсь через greenpois0n сделать (отвязный) произвожу комбинацию кнопок, для перехода в DFU, потом загорается джейлбрейк и при нажатии не происходит не чего, держал хом около минуты, после чего загорается джейл.файлд=(!!!
    Помогите плиз!!!

    vadim_psyduck avatar
    vadim_psyduck9 апреля 2011
    0

    @Arsenbl4, ну по идеи, можно восстановить iphone, джейл привязанный слетит, а потом уже делать через greenpoison

  13. Blue bird avatar
    Blue bird8 апреля 2011
    0

    Отличная статья) Никита, поинтересовался с твоими трудами и нашёл в сети твой блог) а там я увидел статью как ты побывал в Бельдерсайе! в общем классные фотки получились, я сам кстати в феврале отдыхал там:)

    Никита Narmo Дёнин avatar
    0

    @Blue bird, Неожиданный оффтоп :-)

    Blue bird avatar
    Blue bird8 апреля 2011
    0

    @Никита Narmo Дёнин, :)) я тоже не ожидал

  14. check9point avatar
    check9point8 апреля 2011
    0

    Давно хотел программу, которая экран девайса на экране ПК показывает, не подскажешь?

    Никита Narmo Дёнин avatar
    0

    @check9point, Не, таких не знаю.

    severins avatar
    severins8 апреля 2011
    0

    @check9point, Veency в сидии

    check9point avatar
    check9point9 апреля 2011
    0

    @severins, Спасибо, оно самое!

  15. RestinPeace avatar
    RestinPeace8 апреля 2011
    0

    Видео с iPad-а не работает(((

    Tripoll avatar
    Tripoll8 апреля 2011
    0

    @RestinPeace, Неправда, работает.

  16. Tripoll avatar
    Tripoll8 апреля 2011
    0

    Артур, подобные статьи вполне достойны отдельного ярлыка “Разработка” вверху. Привлечет больше iOS-разработчиков на iPhones.ru.

  17. rb avatar
    rb8 апреля 2011
    0

    Будем говорить так:
    это не алерт это просто сообшение да и не ахти красивое.
    анимация применена по старинке во всех рекоминдацих указано использовать блоки.
    алерт должен дисейблить все что под ним
    для этого используют UIWindow с анимацией пульсирования как в родном алерте в сети примеры и сорцы есть этого алерта с кастомным вью.
    очень много лишнего и размусоленого и неправильно примененного
    вот один из
    textLabel.numberOfLines = 0;
    textLabel.lineBreakMode = UILineBreakModeWordWrap;

    врап это перенос на следующую строку а зачем если лайнс 0 ?
    да и зачем писать лайнс 0 если это дефолтное значение?
    а так конечно картинки и много букв и кострубайты анлроид не является примеров
    у него переносы слов в кнопках есть это вообще убожество особенно перенос одной буквы )))
    анлроид это сайт сделаный програмистом где мигает все что можно и чистые цвета
    FF0000 00FF00 0000FF
    андроид никогда не будет примером интерфейсов и проги на айфоне с подобиями андроида это мусор (сори за грубость)

    i.band avatar
    i.band9 апреля 2011
    0

    @rb, вообще это контрол, дизайн можно изменить.
    интерфейс андройда далек от идеала, однако некоторые идеи можно использовать.

    Никита Narmo Дёнин avatar
    0

    @rb, Ну, во-первых, значение numberOfLines по умолчанию не 0, а 1.

    Во-вторых, всё “лишнее” и “размусоленное” сделано потому, что статья носит обучающий характер, и я старался расписать и объяснить все мелкие ньюансы.

    В-третьих, про блоки анимации: ими и сделано, только старыми, потому что новые работают только начиная с 4-ой прошивки (со старыми проще разобраться, потом гораздо проще понять новые блоки).

  18. daffna avatar
    daffna9 апреля 2011
    0

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

    Neverend avatar
    Neverend9 апреля 2011
    0

    @daffna, смешно ты это :) Твою “простую истину” опровергает весь человеческий опыт. Критикуют все и всех, и иногда (в виде исключения) даже по делу. И тут похоже тот самый редкий случай.

  19. UserZMK avatar
    UserZMK9 апреля 2011
    0

    кто знает как получить в AB RIO 4 секретных достижений? в GC написано достижений 38 а видно только 34?!

    UserZMK avatar
    UserZMK9 апреля 2011
    0

    roviomobile пишут на twitter что они достижимы в v1.0.0

  20. Robert avatar
    Robert9 апреля 2011
    0

    Как начинающему будет очень полезно. Спасибо!
    Побольше бы таких статей.

  21. masurfaker avatar
    masurfaker9 апреля 2011
    0

    Пасиба автору!
    Интересно было, хотя не все понял, но при необходимости разобраться – не прлблема!
    Так держать!)

    Никита Narmo Дёнин avatar
    0

    @masurfaker, Будем стараться! Читайте документацию, там хорошо всё написано. Кстати, документация Apple — одна из лучших, что я видел (в плане подробности и удобства навигации).

  22. Santa-Claus_Me avatar
    Santa-Claus_Me9 апреля 2011
    0

    Вам бы на хабр в песочницу, а так я не понимаю что этот пост здесь делает?

    Никита Narmo Дёнин avatar
    0

    @Santa-Claus_Me, На Хабре и так много всякого по разработке. А здесь почти ничего не было. И 2% читателей нравится :-)

  23. Marsianin avatar
    Marsianin9 апреля 2011
    0

    А есть какой-то сайт-каталог к компонентами (платными и бесплатными) под XCode для iPhone и Mac?
    Мне вот нужен полноценный RichView редактор…

  24. Lantego avatar
    Lantego9 апреля 2011
    0

    @Никита Narmo Дёнин, спасибо за ценный метод! Очень актуален. Недавно при продумывании интерфейса как раз столкнулся с необходимостью подобной реализации. Скажи, можно ли использовать этот подход в своем приложении?

    Никита Narmo Дёнин avatar
    0

    @Lantego, Да, можно использовать без каких-либо ограничений.

  25. wipechaos avatar
    wipechaos9 апреля 2011
    0

    Неплохо бы сделать это в виде скрипта или надстройки. Так как я хоть и понимаю что то в юниксе но всё равно разобраться с первого раза будет не просто.

  26. Коротнев Владислав avatar
    0

    Спасиба, я форкнул, буду по своим нуждам дорабатывать. =)

    Никита Narmo Дёнин avatar
    0

    @Коротнев Владислав, Ага, ещё вчера увидел :-)

  27. Rusik avatar
    Rusik9 апреля 2011
    0

    Отличная статья, спасибо автору!

    Только вот есть одно предложение.
    Опубликуйте эту статью еще на TouchDev.ru
    Вы же знаете такой сайт?
    Мне кажется там такие статьи намного более уместны, чем на айфонсру.
    Ну или в крайнем случае кто то другой, или даже я сам туда ее скопирую. Все таки разработчики врядли будут заходить на айфонсру и тут что либо искать. А начинающим разработчикам эта статья так вообще малопонятна и не нужна.

    Вот если бы здесь сделали раздел о разработке под айфон
    и туда бы писало куча людей вот это было бы вообще очень здорово!

    Никита Горяинов avatar
    0

    @Rusik, Ошибаетесь. Посетите наш форум.

    Rusik avatar
    Rusik9 апреля 2011
    0

    @Никита Горяинов, да видел я форум
    неудобный он

  28. Rusik avatar
    Rusik9 апреля 2011
    0

    А разве простым разработчикам разрешено использовать методы начинающиеся с подчеркивания? __animationDidStop:__finished:__context:

    Никита Narmo Дёнин avatar
    0

    @Rusik, Это не API Apple, этот метод я написал сам. Подчёркивания для того, чтобы показать, что этот метод вызывается только внутри компонента. Такой способ принят в Python и мне он нравится.

  29. mr.МУБОРИЗ avatar
    mr.МУБОРИЗ9 апреля 2011
    0

    ОБРАЩЕНИЕ РАЗРАБОТЧИКАМ. ПРОСЬБА :$
    Извините, за то что пишу здесь о вопросе (просто не нашел места, где бы об этом написать)… :)

    Как можно сделать чтобы над иконкой ЗАМЕТКИ был счетчик количества заметок? Аналогично как в приложении СМС или ПОЧТА, когда есть непрочитанное сообщение. А здесь в ЗАМЕТКАХ просто нужно указать СКОЛЬКО внутри заметок и всё.

    P.S. Зачем мне? – Т.К. Использую ЗАМЕТКИ вместо ЗАДАЧ, иногда нужно узнавать сколько осталось дел.

    СПАСИБО!

  30. mr.МУБОРИЗ avatar
    mr.МУБОРИЗ9 апреля 2011
    0

    Жаль :-(

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

Нашли орфографическую ошибку в новости?

Выделите ее мышью и нажмите Ctrl+Enter.

Как установить аватар в комментариях?

Ответ вот здесь