Для отображения всплывающих сообщений в 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.
70 комментариев
Форум →Спасибо за реально интересную статью. Давно таких не было.
А вот интересно, разве всплывающие уведомления которые при изменении звука или при блокировке ориентации появляются, например, использовать нельзя для подобной цели?
Отличная статья. Стоит на хабре запостить, хотя бы в песочницу
@Vizakenjack, на хабр уже не получится: там копипаст даже своего контента запрещён. А вот как топик-ссылку… Кстати, статья великолепна! А то уже устал читать про птиц злобных. Побольше бы таких статей!
Вообщето разрешение iPhone 4 960×640.
@Theodore Plesha, На программном уровне в iPhone оно 320×480 для любого экрана.
@Никита Narmo Дёнин, ненене. разрешение будет 960 на 640 пикселей, но 320 на 240 поинтов. Это сделано для удобства разработчиков, т.к. 1 поинт на 4м айфоне будет считаться за квадрат 2 на 2 пикселя, а на 3Гс — за один пиксель.
Однако есть @property CGFloat contentScaleFactor; которое возвращает «масштаб» (сколько пикселей в поинте для данного view на экране).
Так что можно использовать и пиксели, но обычно они используются для 3Д объектов, например. А для задания размеров как в нашем примере — поинты :)
@i.band, Я про поинты и говорил как раз.
@Никита Narmo Дёнин, я понял)) просто решил еще раз расставить все точки над i :)
PS спасибо за статью. очень нравятся такие статьи.
PPS там наверху коммент оставил про всплывающие полупрозрачные нотификаторы (при изменении громкости боковыми кнопками и тп). они доступны в API? их можно для подобной цели использовать? :)
@i.band, Нет, к сожалению.
прикольная штукень, в разы лучше и удобней чем текущие окна с сообщениями
Хорошая статья.
Надеюсь в будущем такие статьи будут появляться еще
@TauruS_iPhone4_now, Я тоже надеюсь :-) Надо придумать, о чём ещё написать.
@Никита Narmo Дёнин, идея: Такой твик в сидию :)
@iL0bster, Не, тут я пас.
@Никита Narmo Дёнин, а что так? Многие были бы довольны.
@iL0bster, Я на таком низком уровне не пишу. Я больше по прикладным приложениям.
@Никита Narmo Дёнин, Не ужели всё так принципиально?
Я вот бы тоже не отказался от такого твика)
@Никита Narmo Дёнин, надо придумать как это прикрутить в айос
Спасибо за статью! Надеюсь вы дальше тоже будите выкладывать подобные уроки, очень полезно!
Говорю спасибо автору, что хоть как-то разбавляет контент интересными статьями. Кстати могу подкинуть пару идей, про что писать дальше =)
@zanzy, Давай :-) Можно на форуме в личку (мой ник — Narmo).
Отличная статься! Спасибо! Нужно копию в Apple послать! Пускай пересмотрят свои всплывающие окна!
Автору ряльно огромной спасибо. Велекопный твик. Давно таких статей небыло. А главное 1ой строчкой вызов – ммм.
печаль ) залип за клавой, сор за орфографию :)
@RoLife, Спасибо! Я — за лаконичность вызовов :-)
Статья хорошая, только зачем она здесь? Для этого ведь есть форум, там и обсуждать удобнее.
@MAXiDROME, а чего тут обсуждать? Все и так понятно.
@Артур Малосиев, много приложений под айфон разработал, что все понятно? )))))))
не подскажите, где можно научится всему этому? очень хочется писать программы под айфон, а я умею только сайты…
@berkutiv, Попробуй начать с раздела для разработчиков здесь, на форуме. И не бойся экспериментировать. Поставь Xcode, почитай документацию от Apple. Практически к каждому разделу их документации имеются примеры.
@Никита Narmo Дёнин, аа..спасибо:)
@berkutiv, и книги можно почитать. сейчас даже выходят русские. плюс iTunes U с лекциями. И google.com :)
Простите, что не в тему=(( Сделал джейл 4.2.1 через Redsn0w (привязанный), пытаюсь через greenpois0n сделать (отвязный) произвожу комбинацию кнопок, для перехода в DFU, потом загорается джейлбрейк и при нажатии не происходит не чего, держал хом около минуты, после чего загорается джейл.файлд=(!!!
Помогите плиз!!!
@Arsenbl4, ну по идеи, можно восстановить iphone, джейл привязанный слетит, а потом уже делать через greenpoison
Отличная статья) Никита, поинтересовался с твоими трудами и нашёл в сети твой блог) а там я увидел статью как ты побывал в Бельдерсайе! в общем классные фотки получились, я сам кстати в феврале отдыхал там:)
@Blue bird, Неожиданный оффтоп :-)
@Никита Narmo Дёнин, :)) я тоже не ожидал
Давно хотел программу, которая экран девайса на экране ПК показывает, не подскажешь?
@check9point, Не, таких не знаю.
@check9point, Veency в сидии
@severins, Спасибо, оно самое!
Видео с iPad-а не работает(((
@RestinPeace, Неправда, работает.
Артур, подобные статьи вполне достойны отдельного ярлыка “Разработка” вверху. Привлечет больше iOS-разработчиков на iPhones.ru.
Будем говорить так:
это не алерт это просто сообшение да и не ахти красивое.
анимация применена по старинке во всех рекоминдацих указано использовать блоки.
алерт должен дисейблить все что под ним
для этого используют UIWindow с анимацией пульсирования как в родном алерте в сети примеры и сорцы есть этого алерта с кастомным вью.
очень много лишнего и размусоленого и неправильно примененного
вот один из
textLabel.numberOfLines = 0;
textLabel.lineBreakMode = UILineBreakModeWordWrap;
врап это перенос на следующую строку а зачем если лайнс 0 ?
да и зачем писать лайнс 0 если это дефолтное значение?
а так конечно картинки и много букв и кострубайты анлроид не является примеров
у него переносы слов в кнопках есть это вообще убожество особенно перенос одной буквы )))
анлроид это сайт сделаный програмистом где мигает все что можно и чистые цвета
FF0000 00FF00 0000FF
андроид никогда не будет примером интерфейсов и проги на айфоне с подобиями андроида это мусор (сори за грубость)
@rb, вообще это контрол, дизайн можно изменить.
интерфейс андройда далек от идеала, однако некоторые идеи можно использовать.
@rb, Ну, во-первых, значение numberOfLines по умолчанию не 0, а 1.
Во-вторых, всё “лишнее” и “размусоленное” сделано потому, что статья носит обучающий характер, и я старался расписать и объяснить все мелкие ньюансы.
В-третьих, про блоки анимации: ими и сделано, только старыми, потому что новые работают только начиная с 4-ой прошивки (со старыми проще разобраться, потом гораздо проще понять новые блоки).
@rb, усвой одну простую истину ругать можно только если ты можешь лучше. да и то после этого нужна ссылка с тем как это лучше можешь реализовать ты
@daffna, смешно ты это :) Твою “простую истину” опровергает весь человеческий опыт. Критикуют все и всех, и иногда (в виде исключения) даже по делу. И тут похоже тот самый редкий случай.
кто знает как получить в AB RIO 4 секретных достижений? в GC написано достижений 38 а видно только 34?!
roviomobile пишут на twitter что они достижимы в v1.0.0
Как начинающему будет очень полезно. Спасибо!
Побольше бы таких статей.
Пасиба автору!
Интересно было, хотя не все понял, но при необходимости разобраться – не прлблема!
Так держать!)
@masurfaker, Будем стараться! Читайте документацию, там хорошо всё написано. Кстати, документация Apple — одна из лучших, что я видел (в плане подробности и удобства навигации).
Вам бы на хабр в песочницу, а так я не понимаю что этот пост здесь делает?
@Santa-Claus_Me, На Хабре и так много всякого по разработке. А здесь почти ничего не было. И 2% читателей нравится :-)
А есть какой-то сайт-каталог к компонентами (платными и бесплатными) под XCode для iPhone и Mac?
Мне вот нужен полноценный RichView редактор…
@Никита Narmo Дёнин, спасибо за ценный метод! Очень актуален. Недавно при продумывании интерфейса как раз столкнулся с необходимостью подобной реализации. Скажи, можно ли использовать этот подход в своем приложении?
@Lantego, Да, можно использовать без каких-либо ограничений.
Неплохо бы сделать это в виде скрипта или надстройки. Так как я хоть и понимаю что то в юниксе но всё равно разобраться с первого раза будет не просто.
Спасиба, я форкнул, буду по своим нуждам дорабатывать. =)
@Коротнев Владислав, Ага, ещё вчера увидел :-)
Отличная статья, спасибо автору!
Только вот есть одно предложение.
Опубликуйте эту статью еще на TouchDev.ru
Вы же знаете такой сайт?
Мне кажется там такие статьи намного более уместны, чем на айфонсру.
Ну или в крайнем случае кто то другой, или даже я сам туда ее скопирую. Все таки разработчики врядли будут заходить на айфонсру и тут что либо искать. А начинающим разработчикам эта статья так вообще малопонятна и не нужна.
Вот если бы здесь сделали раздел о разработке под айфон
и туда бы писало куча людей вот это было бы вообще очень здорово!
@Rusik, Ошибаетесь. Посетите наш форум.
@Никита Горяинов, да видел я форум
неудобный он
А разве простым разработчикам разрешено использовать методы начинающиеся с подчеркивания? __animationDidStop:__finished:__context:
@Rusik, Это не API Apple, этот метод я написал сам. Подчёркивания для того, чтобы показать, что этот метод вызывается только внутри компонента. Такой способ принят в Python и мне он нравится.
ОБРАЩЕНИЕ РАЗРАБОТЧИКАМ. ПРОСЬБА :$
Извините, за то что пишу здесь о вопросе (просто не нашел места, где бы об этом написать)… :)
Как можно сделать чтобы над иконкой ЗАМЕТКИ был счетчик количества заметок? Аналогично как в приложении СМС или ПОЧТА, когда есть непрочитанное сообщение. А здесь в ЗАМЕТКАХ просто нужно указать СКОЛЬКО внутри заметок и всё.
P.S. Зачем мне? – Т.К. Использую ЗАМЕТКИ вместо ЗАДАЧ, иногда нужно узнавать сколько осталось дел.
СПАСИБО!
Жаль :-(
Нашли орфографическую ошибку в новости?
Выделите ее мышью и нажмите Ctrl+Enter.Мышь с колесом прокрутки в macOS Sierra стала работать медленно, пропала плавная прокрутка
Как добавить второе лицо для разблокировки по Face ID
iPhone неверно поворачивает интерфейс при наклоне?
Как добавить в iPhone события из календаря Google
Как на iPhone автоматически принимать телефонные звонки и вызовы в мессенджерах
Как восстановить закрепленное сообщение в группе Telegram, если случайно закрыл его
Как на Mac включить отображение строки меню в полноэкранном режиме
Как на iPad включить режим ввода дополнительных символов на виртуальной клавиатуре