Pull to refresh

Пишем оповещение для iOS

Reading time5 min
Views2.2K
Доброе утро/день/вечер/ночь %username%!

Предыстория

В процессе реализации очередного проекта появилась необходимость реализовать эффективное оповещение пользователя о чём-либо (например, об отсутствии интернет соединения). Так как же это сделать? Стандартный класс UIAlertView оказался для этой цели слишком громоздким и скучным, и, не найдя более ничего подходящего, было решено написать своё казино с блекдже… свой класс оповещения.

Реализация

Итак, приступим. Главным критерием для данного класса стал «вызов одной строчкой кода», соответственно я получил метод:

+ (void) showMessage:(NSString *)message;


Исходя из поставленной задачи «очень простого оповещения пользователя» было решено использовать:
  • UILabel — 1шт.
  • UIView — 2шт.


Для начало мы возьмём UILabel и «заточим» под свои нужды:

UILabel *messageLabel = [[UILabel alloc] init];
messageLabel.textAlignment = UITextAlignmentCenter;
messageLabel.numberOfLines = 0;
messageLabel.lineBreakMode = UILineBreakModeWordWrap;
messageLabel.text = message;
messageLabel.font = [UIFont fontWithName:@”Helvetica-Bold” size:15.0f];
messageLabel.textColor = [UIColor whiteColor];
messageLabel.backgroundColor = [UIColor clearColor];


Дальше необходимо узнать – «А сколько же наше сообщение займет места на экране?»

CGSize messageSize = [message sizeWithFont:messageLabel.font constrainedToSize:CGSizeMake(160.0f, 9999.0f) lineBreakMode:UILineBreakModeWordWrap];


Метод для NSString sizeWithFont:(UIFont *)font constrainedToSize(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode вернет нам в данном случае размеры прямоугольника, в который будет «вписан текст» исходя из условий:
  • многострочность
  • ширина блока равна 160.0f (может быть больше, в случае, если слово не умещается в этот размер)


Дальше необходимо из полученных размеров сделать сам прямоугольник с учетом 2х дополнительных размеров:
  • Толщина границы родительского элемента (в него мы «положим» UILabel)
  • Отступ от границ родительского элемента


CGRect messageRect = CGRectMake(offsetSize.width + borderWidth, offsetSize.height + borderWidth, messageSize.width, messageSize.height); 
messageLabel.frame = messageRect;


Всё, на этом пока что мы закончили с UILabel.
Дальше на очереди контейнер для UILabel – первый из двух UIView. Для контейнера необходимо было учесть – цвет фона, граница, отступы от границы. Как дополнение к дизайну «оповещалки» — скругленные углы, тень и прозрачность. Ниже приведен код нашего контейнера

messageSize.width += offsetSize.width*2.0f + borderWidth;
messageSize.height += offsetSize.height*2.0f + borderWidth;
contentRect = CGRectMake(0.0f, 0.0f, messageSize.width, messageSize.height);
UIView *content = [[UIView alloc] init];
content.frame = contentRect;
content.backgroundColor = [UIColor colorWithRed:(60.0f/255.0f) green:(60.0f/255.0f) blue:(60.0f/255.0f) alpha:1.0f];
content.alpha = 0.8f;
content.layer.cornerRadius = 8.0f;
content.layer.shadowRadius = 8.0f;
content.layer.masksToBounds = NO;
content.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
content.layer.shadowOpacity = 1.0f;
content.layer.borderWidth = 1.0f; 
content.layer.borderColor = [[UIColor colorWithRed:(128.0f/255.0f) green:(128.0f/255.0f) blue:(128.0f/255.0f) alpha:1.0f] CGColor];
content.layer.shadowPath = [UIBezierPath bezierPathWithRect:contentRect].CGPath;


На этом всё. Теперь нам остается задействовать лишь последний оставшийся UIView – rootView. Его мы будем использовать для анимации.

UIView *rootView = [[UIView alloc] init];
rootView.tag = 2000;
rootView.frame = contentRect;
rootView.alpha = 0.0f;


Всё на этом этапе у нас есть подготовленные для дальнейшего использования UILabel с сообщением и 2 UIView контейнера. Осталось только собрать из них матрешку и установить позицию в центре экрана:

messageLabel.center = CGPointMake(contentRect.size.width/2.0f, contentRect.size.height/2.0f);
[content addSubview:messageLabel];
[messageLabel release];
[rootView addSubview:content];
[content release];
rootView.center = [[UIApplication sharedApplication] keyWindow].center;


Анимация

В нашем случае анимация была разбита на 3 составляющие:
  • Появление
  • Имитация bounce-эффекта
  • Исчезнование


Bounce-эффект достигался простой анимацией увеличения rootView с размеров с коэффициентом 0.8 до размеров с коэффициентом 1.1 и немедленным уменьшением до естественного размера – коэффициент 1.0. Появление и исчезновение – изменение rootView.alpha с 0.0f до 1.0f и наоборот. Исчезновение так же сопровождается изменением размеров до коэффициента 1.2. Для появления и достижения «пиковой» точки bounce-эффекта (а именно когда будет достигнут коэффициент 1.2) было решено отвести 2/3 общего времени запланированного на анимацию. Остаток времени был использован на переход к нормальному отображению. Дальше пользователю отведено время для возможности прочесть сообщение и в последствии исчезновение сообщения
Начиная с iOS версии 4.0 анимация реализовывается через блоки. Поэтому требуемая версия iOS не ниже 4.0 (в дальнейшем планируется добавить поддержку iOS 3).

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion


метод класса UIView позволяет нам установить:
  • продолжительность анимации
  • продолжительность паузы перед анимацией
  • настройки анимации
  • непосредственно саму анимацию
  • и код, который будет выполнен по завершению анимации


Код реализации анимации приведен ниже:

rootView.transform = CGAffineTransformMakeScale(0.8f, 0.8f);
[[[UIApplication sharedApplication] keyWindow] addSubview:rootView];
[UIView animateWithDuration:0.35*0.66 
		delay:0.0 
		options:UIViewAnimationCurveEaseOut
		animations:^ {
			rootView.alpha = 0.66f;
			rootView.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
		} 
		completion:^(BOOL completed) {
			[UIView animateWithDuration:0.35*0.33 
				delay:0.0 
				options:UIViewAnimationCurveLinear 
				animations:^{
					rootView.alpha = 1.0f;
					rootView.transform = CGAffineTransformIdentity;
				} 
				completion:^(BOOL completed) {
					[UIView animateWithDuration:0.35
					delay:0.9											options:UIViewAnimationCurveEaseIn
					animations:^{
						rootView.alpha = 0.0f;
						rootView.transform = CGAffineTransformMakeScale(1.2f, 1.2f);
					}
					completion:^(BOOL completed) {
						[rootView removeFromSuperview];
						[rootView release];
					}];
			}];
}];


Итог
В результате написания данного класса мы получили очень удобную “оповещалку”, которую можно вызвать одной строчкой кода:

[FHNotification showMessage:@”Very simple inApp-notification”];


Выводы

Плюсы данного класса:
  • Очень и очень простой класс в использовании

Минусы:
  • Поддержка только iOS 4.0+
  • Возможность только текстового оповещения


Для работы данного класса так же потребуется QuartzCore.framework
Исходный код можно скачать на github, немного дополненный и расширенный
На этом всё. Спасибо за внимание
Tags:
Hubs:
Total votes 34: ↑29 and ↓5+24
Comments11

Articles