iPhone SDK: сборник подсказок

Вот уже полгода я занимаюсь разработкой под iPhone. За это время я недалеко вышел за пределы необходимого при написании OpenGL-игр и не стал большим знатоком Objective C, так как имею возможность писать всё на любимом C++. Тем не менее, регулярно попадались достаточно специфичные задачи. В этой записе собраны некоторые подсказки осваивающим платформу, да и мне самому на будущее, как некоторые из таких задач решать. Всего 5 пунктов.

1. Как обработать изменение ориентации устройства?

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

Решение: самый простой, доступный и понятный способ — подписаться на событие UIDeviceOrientationDidChangeNotification. Сделать это можно так:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];

Обработчик в таком случае будет выглядеть так:

- (void)didRotate:(NSNotification *)notification
{
	UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
 
	// Обработать изменение ориентации...
}

В любой момент времени текущую ориентацию устройства можно получить через свойство orientation объекта UIDevice.

2. Как сделать ссылку на веб-страницу?

Задача: по какому-то событию (например, нажатию кнопки) закрыть программу и открыть браузер на заданной странице.

Решение: всё предельно просто:

NSURL *url = [[NSURL alloc] initWithString:@"http://wicharek.name"];
[[UIApplication sharedApplication] openURL:url];

3. Как программно создать письмо в почтовом клиенте?

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

Решение: всё делается через openUrl по протоколу mailto. Но с использованием HTML есть ряд нюансов. Поступать рекомендуется следующим образом:

// Это тело нашего сообщения
NSString *htmlBody = @"Look at this cool game! <a href="http://example.com">Cool Game Web-Site</a>";
 
// Тело сообщения мы кодируем средствами Core Foundation
NSString *escapedBody = [(NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
   (CFStringRef)htmlBody, NULL,  CFSTR("?=&amp;+"), CFStringEncodingUTF8) autorelease]; 
 
// Задаём префикс сообщения. Тут мы можем указать получателя и тему письма
// Префикс кодируем при помощи метода в NSString
NSString *mailtoPrefix = [@"mailto:example@example.com?subject=Cool Game&amp;body="
    stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];  
 
// Объединяем тело сообщения и префикс
NSString *mailtoStr = [mailtoPrefix stringByAppendingString:escapedBody];  
 
// Открываем URL
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailtoStr]];

4. Как сделать из UIImage текстуру для использования в OpenGL ES?

Задача: имеется объект UIImage, необходимо сделать из него OpenGL текстуру, чтобы в последующем её можно было использовать для рендеринга. Предполагаем, что размеры картинки отвечают требованиям к размеру текстуры (ширина и высота равны степени двойки и равны между собой). Предполагаем также, что нам нужно получить 32-битную текстуру (RGBA).

Решение: предположим у нас имеется валидный UIImage, загруженный, к примеру, из файла:

UIIImage* image = [UIImage imageWithContentsOfFile:@"example.png"];

Сначала нам необходимо получить «сырые» данные для будущей текстуры:

// Прежде всего преобразуем UIImage к CGImage
// Эта операция не производит копирования всей текстуры, а значит достаточно легковесна
CGImageRef cg_image = [image CGImage];
 
// Создаём массив данных для текстуры
char* texture_data = new char[image.size.width * image.size.height * 4];
 
// Вся магия происходит тут
CGContext* texture_context = CGBitmapContextCreate(texture_data, image.size.width, image.size.height, 8,
	image.size.width * 4, CGImageGetColorSpace(cg_image), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(texture_context, CGRectMake(0.0, 0.0, (CGFloat)image.size.width,
	(CGFloat)image.size.height), cg_image);
CGContextRelease(texture_context);

По итогам имеем texture_data с «сырыми» пикселями. Передаём их в OpenGL:

GLuint texture_name; // "имя" текстуры
 
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &amp;texture_name);
glBindTexture(GL_TEXTURE_2D, texture_name);
 
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size.width, image.size.height, 0,
	GL_RGBA, GL_UNSIGNED_BYTE, texture_data);

5. Как нарисовать только часть UIImage?

Задача: имеется объект UIImage. Необходимо нарисовать его прямоугольный фрагмент в текущем контексте.

Решение: нарисовать картинку целиком труда не составляет — у UIImage есть метод drawInRect. Рисовать же только часть картинки, по мнению Apple, разработчику не надо. Делается это следующим образом:

-(void)drawImage:(UIImage*)image part:(CGRect)sourceRect inRect:(CGRect)destRect
{
	// Создаём ссылку на заданную часть картинки
	CGImageRef cg_image_part = CGImageCreateWithImageInRect([image CGImage], sourceRect);
	// Преобразуем её к UIImage
	UIImage* ui_image_part = [UIImage imageWithCGImage:cg_image_part];
	// Рисуем в заданном прямоугольнике
	[ui_image_part drawInRect:destRect];
 
	[ui_image_part release];
	CGImageRelease(cg_image_part);
}

Бонус. Особенности обработки множественный касаний.

Не всем известно, что iPhone не обрабатывает больше 5-ти одновременных касаний при включенном мультитаче. При чём ведёт себя в такой ситуации достаточно странно: когда пользователь пытается делает 6-е касание, все предыдущие касания отменяются, при чём событие touchesCancelled приходит по два раза для каждого из касаний (по крайней мере, в прошивке 2.1, на более новых не пробовал). Будьте бдительны.

Опубликовать в Google Plus
Опубликовать в LiveJournal

Комментарии

  1. avatar
    Компьютерщик

    Благодарю за информацию. Удачи Вам!

  2. avatar
    Никита Рыбин

    Увлекательно. Хотелось бы еще чего-нибудь интересного на эту же тему.

  3. avatar
    Борис Петров

    Просто отлично написано! Надобыотметить на БобрДобр. :)

  4. Спасибо

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">