Вот уже полгода я занимаюсь разработкой под 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("?=&+"), CFStringEncodingUTF8) autorelease]; // Задаём префикс сообщения. Тут мы можем указать получателя и тему письма // Префикс кодируем при помощи метода в NSString NSString *mailtoPrefix = [@"mailto:example@example.com?subject=Cool Game&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, &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, на более новых не пробовал). Будьте бдительны.
wicharek
Благодарю за информацию. Удачи Вам!
Увлекательно. Хотелось бы еще чего-нибудь интересного на эту же тему.
Просто отлично написано! Надобыотметить на БобрДобр. :)
Спасибо