Pull to refresh

Разработка виджета для центра уведомлений iOS

Reading time7 min
Views6.8K


Notification Center — удобная и простая в использовании функция в iOS, но она ограничена лишь стандартными виджетами. Не секрет, что любители jailbreak уже давно пользуются сторонними твиками, но вот информации о их разработке практически нет. В этой статье я постараюсь это исправить и описать процесс создания на примере виджета для проверки баланса моего интернет-провайдера.


Для разработки виджета нам нужен Xcode, подопытный девайс с iOS 5.x и доступом к его файловой системе, а в качестве шаблона виджета мы воспользуемся набором iOSOpenDev, подробная инструкция по его установке находится на сайте разработчика.

Если все готово, приступаем!

Запускаем Xcode и создаем новый проект:
Открываем пункт iOSOpenDev в категории iOS и выбираем NotificationCenter Widget.



Итак, перед нами код уже готового (но пустого) виджета. Мы уже можем его скомпилировать, нажав Cmd+Shift+I (или выбрав в меню Product > Build for > Build for profiling), в результате чего получим установочный .deb файл, который находится в директории Наш Проект/Packages.

В свойствах проекта можно сделать так, чтобы файл сразу устанавливался на девайс, для этого нужен SSH и Wi-Fi, но эта статья была написана там, где беспроводной сети не оказалось, поэтому нам понадобится любой iOS файловый менеджер (например iExplorer) и установленный на девайсе iFile.

Копируем на девайс в любое удобное для Вас место наш (пока еще пустой) виджет, и устанавливаем его через iFile, затем делаем Respring или перезагрузку девайса, это необходимо, чтобы Notification Center увидел наш виджет.

Теперь, если открыть Notification Center, то… виджета там не будет, его еще надо включить. Открываем Settings > Notifications > Ваш Виджет и включаем его!


На этом введение закончено.
Начинаем писать виджет проверки баланса!

Официально мой провайдер не предоставляем API для проверки состояния счета, но я подсмотрел нужный код в гаджете для Windows.
Процесс довольно прост, открывается https, в адресе которого указывается логин и пароль, в ответ приходит xml.

Стандартный виджет погоды имеет интересную особенность, его можно «пролистывать» для просмотра погоды на неделю, мы возьмем эту идею для размещения двух UITextFiled (логин/пароль).

Добавим в .h файл следующий код:
	UIScrollView *sv;
	UIView *balanceView, *settingsView;
	
	UITextField * loginField;
	UITextField * passField;


А в файле .m добавим несколько UILabel и поправим код вот на этот:

- (UIView *)view
{
	if (_view == nil)
	{
		_view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 71)];
		
		UIImage *bg = [[UIImage imageWithContentsOfFile:@"/System/Library/WeeAppPlugins/balance.bundle/WeeAppBackground.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:71];

		UIImageView *bgView = [[UIImageView alloc] initWithImage:bg];
		bgView.frame = CGRectMake(2, 0, 316, 71);

		UIImageView *bgSet = [[UIImageView alloc] initWithImage:bg];
		bgSet.frame = CGRectMake(6, 0, 316, 71);

		sv = [[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 71)] autorelease];
		sv.contentSize = CGSizeMake(2 * 320, 71);
		sv.pagingEnabled = YES;
		sv.delegate = self;

		for (int i = 0; i < 1; i++)
		{
			balanceView = [[UIView alloc] initWithFrame:CGRectMake(i * 316, 0, 316, 71)];
			[balanceView addSubview:bgView];
			
			lblName = [[UILabel alloc] initWithFrame:CGRectMake(9, 5, 285, 15)];
			lblName.backgroundColor = [UIColor clearColor];
			lblName.textColor = [UIColor whiteColor];
			lblName.font = [UIFont systemFontOfSize: 12.0];
			lblName.text = @"Фамилия Имя Отчество";
			lblName.alpha = 1;
			[balanceView addSubview:lblName];
			[lblName release];
			
			lblText = [[UILabel alloc] initWithFrame:CGRectMake(9, 18, 75, 15)];
			lblText.backgroundColor = [UIColor clearColor];
			lblText.textColor = [UIColor whiteColor];
			lblText.font = [UIFont systemFontOfSize: 12.0];
			lblText.text = @"Ваш баланс:";
			lblText.alpha = 0.6;
			[balanceView addSubview:lblText];
			[lblText release];
			
			lblDate = [[UILabel alloc] initWithFrame:CGRectMake(9, 38, 114, 28)];
			lblDate.backgroundColor = [UIColor clearColor];
			lblDate.textColor = [UIColor whiteColor];
			lblDate.font = [UIFont systemFontOfSize: 12.0];
			lblDate.numberOfLines = 2;
			lblDate.text = @"Хватит примерно на 0 дней";
			lblDate.alpha = 0.6;
			[balanceView addSubview:lblDate];
			[lblDate release];
			
			lblBalance = [[UILabel alloc] initWithFrame:CGRectMake(135, 22, 175, 45)];
			lblBalance.backgroundColor = [UIColor clearColor];
			lblBalance.textColor = [UIColor whiteColor];
			lblBalance.font = [UIFont fontWithName: @"Helvetica-Light" size: 45.0];
			lblBalance.textAlignment = UITextAlignmentRight;
			lblBalance.text = @"0.0";
			lblBalance.alpha = 1;
			[balanceView addSubview:lblBalance];
			[lblBalance release];
			
			[sv addSubview:balanceView];
			[balanceView release];
		}
		for (int i = 1; i < 2; i++)
		{
			settingsView = [[UIView alloc] initWithFrame:CGRectMake(i * 316, 0, 316, 71)];
			[settingsView addSubview:bgSet];
			
			loginField = [[UITextField alloc] initWithFrame:CGRectMake(330, 7, 300, 25)];
			loginField.borderStyle = UITextBorderStyleRoundedRect;
			loginField.textColor = [UIColor blackColor];
			loginField.font = [UIFont systemFontOfSize:14.0];
			loginField.placeholder = @"Логин";
			loginField.backgroundColor = [UIColor whiteColor];
			loginField.autocorrectionType = UITextAutocorrectionTypeNo;
			loginField.keyboardType = UIKeyboardTypeDefault; 
			loginField.returnKeyType = UIReturnKeyNext;
			loginField.clearButtonMode = UITextFieldViewModeWhileEditing;
			loginField.keyboardAppearance = UIKeyboardAppearanceAlert;
			loginField.delegate = self;
			loginField.tag = 999;
			[sv addSubview:loginField];

			passField = [[UITextField alloc] initWithFrame:CGRectMake(330, 39, 300, 25)];
			passField.borderStyle = UITextBorderStyleRoundedRect;
			passField.textColor = [UIColor blackColor];
			passField.font = [UIFont systemFontOfSize:14.0];
			passField.placeholder = @"Пароль";
			passField.secureTextEntry = TRUE;
			passField.backgroundColor = [UIColor whiteColor];
			passField.autocorrectionType = UITextAutocorrectionTypeNo
			passField.keyboardType = UIKeyboardTypeDefault; 
			passField.returnKeyType = UIReturnKeyDone; 
			passField.clearButtonMode = UITextFieldViewModeWhileEditing;
			passField.keyboardAppearance = UIKeyboardAppearanceAlert;
			passField.delegate = self;
			[sv addSubview:passField];
			
			[sv addSubview:settingsView];
			[settingsView release];			
		}
		
		[bgView release];
		[_view addSubview:sv];

		[[loginField superview] bringSubviewToFront:loginField];
		[[passField superview] bringSubviewToFront:passField];

		[sv setShowsHorizontalScrollIndicator:NO];
		[sv setShowsVerticalScrollIndicator:NO];
	}

	return _view;
}


Таким образом мы создали UIScrollView, добавили на него 2 обычных View, присвоили им background и разместили не первом несколько лэйблов, а на втором два UITextField для ввода логина и пароля.
Далее мы добавим функциональности нашим UITextField, сделаем переход с поля Логин на поле Пароль через кнопку Next и сохранение данных на кнопку Done на клавиатуре:

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
	if (textField.tag == 999) {
		[passField becomeFirstResponder];
	} else {
		
		NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
		[prefs setObject:loginField.text forKey:@"login"];	
		[prefs setObject:passField.text forKey:@"pass"];	
		[prefs synchronize];

		[textField resignFirstResponder];
	}
	
    return YES;
}


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

- (void) scrollViewDidScroll: (UIScrollView *) aScrollView
{
	[loginField resignFirstResponder];
	[passField resignFirstResponder];
}


Теперь приступим к парсеру, в начале сделаем так, чтобы активность виджета начиналась не тогда, когда юзер только начнет открывать NC, а через определенное время. Воспользуемся таймером. Запустим его в момент открытия NC:

- (void)viewWillAppear 
{
	[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(getBalance) userInfo:nil repeats:NO];
}


И через одну секунду выполняем getBalance:

- (void)getBalance 
{
	NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
	NSString *gnLookup = [NSString stringWithFormat:
						  @"https://billing.novotelecom.ru/billing/user/api/?method=userInfo&login=%@&password=%@", 
						  [prefs objectForKey:@"login"], 
						  [prefs objectForKey:@"pass"]];
	
	NSXMLParser *gnParser = [[NSXMLParser alloc] initWithContentsOfURL: [NSURL URLWithString:gnLookup]];
	[gnParser setDelegate:self];
	[gnParser parse];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
	
	if ([elementName compare:@"name"] == NSOrderedSame) {
		ntkUser = [[NSMutableString alloc] initWithCapacity:4];
	}	
	if ([elementName compare:@"days2BlockStr"] == NSOrderedSame) {
		ntkUpToDate = [[NSMutableString alloc] initWithCapacity:4];
	}
	if ([elementName compare:@"balance"] == NSOrderedSame) {
		ntkBalance = [[NSMutableString alloc] initWithCapacity:4];
	}
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
	if (ntkUser && string) {
		[ntkUser appendString:string];
	}	
	if (ntkUpToDate && string) {
		[ntkUpToDate appendString:string];
	}
	if (ntkBalance && string) {
		[ntkBalance appendString:string];
	}
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
	
	if ([elementName compare:@"name"] == NSOrderedSame) {
		lblName.text = [NSString stringWithFormat:@"%@", ntkUser];
	}	
	if ([elementName compare:@"days2BlockStr"] == NSOrderedSame) {
		lblDate.text = [NSString stringWithFormat:@"%@", ntkUpToDate];
	}
	if ([elementName compare:@"balance"] == NSOrderedSame) {
		lblBalance.text = [NSString stringWithFormat:@"%@", ntkBalance];
	}			
}


Жмем Cmd+Shift+I, копируем deb, устанавливаем и делаем респринг (он нужен каждый раз, если Вы устанавливаете виджет через deb файл).



На этом, пожалуй, все, виджет можно и нужно совершенствовать, добавить проверку на подключение к интернету, улучшить отображение текстфилдов, добавить тень на лэйблы и прочее, как визуальное, так и программное. Но суть статьи в том, чтобы подтолкнуть людей к написанию виджетов, и может быть, в один прекрасный день, Apple все же откроет нам API Notification Center.

Код на GitHub

Успехов!
Tags:
Hubs:
Total votes 72: ↑63 and ↓9+54
Comments29

Articles