Показаны сообщения с ярлыком Image. Показать все сообщения
Показаны сообщения с ярлыком Image. Показать все сообщения

среда, 8 января 2014 г.

Эффект листания изображения для Iphone, IOS 5+

В этой статье я расскажу как сделать эффект листания изображений как страниц в книге или в отрывном календаре. Очень часто приходится сталкиваться с задачами реализовать мини галерею или список изображений. Что бы сделать интерфейс красивым и понятным для пользователя есть множество путей. Сегодня я покажу как быстро и просто сделать листание изображений, что бы они переворачивались как листы при помощи протягивания пальцем по экрану, Этот принцип конечно же построен на работе с GestureRecognizer.

Для начала создайте проект в Xcode выбрав Single View Application. (Я работаю в Xcode 4.6 поэтому буду показывать на примере с использованием Storyboard)
Название не имеет значения. Ставим галочку Use Storyboards и сохраняем проект где нибудь себе в папку. Так у нас создастся наш коренвой ViewController  с именем которое вы указали в качестве названия приложения и наш  Storyboard.


Давайте сразу приступим к созданию UIimageView который будет перелистыватся. Перейдите на ваш Storyboard он пока пустой и добавим мы в него пока всего одну картинку при помощи  элемента  UIimageView Найдите его в списке объектов и разместите на вашей Storyboard.

Теперь в h файле вашего контроллера необходимо создать IBOutlet для связи с нашим UIImageView
Вот как это выглядит у меня:
@interface testViewController : UIViewController{
IBOutlet UIImageView *myimage;
}
Теперь можно назначить New Referensing Outlet Collections нашему myimage.

 Затем выбираем New Referensing Outlet Collections нажимая левой кнопкой мышки на кружочке и тянем к левому блоку в наш UIView. И связываем с myimage.

Теперь UIImageView можно назначить картинку и сделать это можно через опять таки Storyboard, как это показано на рисунке.
Также очень важно не забыть поставить галочку Use Interaction Enabled

Далее переходим к m файлу нашего контроллера и находим в нем метод – (void)viewDidLoad
Добавим туда  код который по сути привяжет использования жеста Swipeк нашей картинке и заставит её переворачиваться в зависимости от горизонтальных движений пальцами по экрану телефона.
 [myimage setUserInteractionEnabled:YES];
//перетащили пальцем вправо
UISwipeGestureRecognizer *swipenext = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:@selector(getNext)];
[swipenext setDirection:UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionLeft];
[myimage addGestureRecognizer:swipenext];
//перетащили пальцем влево
UISwipeGestureRecognizer *swipeback = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:@selector(getPrev)];
[swipeback setDirection:UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionRight];
[myimage addGestureRecognizer:swipeback];
Теперь подробней поговорим о коде.
UISwipeGestureRecognizer *swipenext = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:@selector(null)]; – создаёт объект жеста swipenext с параметрами
Обратите внимание на параметр action:@selector(getNext) – сдесь мы можем указать метод который выполнится после перелистывания например это может быть показать следующую картинку getNext.  Посмотрим реализацию этого метода
-(void) getNext{
//устанавливаем анимацию переворачивания
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1]; // сколько длится анимация – в секундах
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:myimage cache:YES]; //овернуть «листок» вверх
[UIView commitAnimations];
}
// почти тоже самое в метотоде getPrev
-(void) getPrev{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:myimage cache:YES]; //отвернуть листок вниз
[UIView commitAnimations];
}
Теперь запустим проект и потягаем мышкой по экрану эмулятора (или телефона если вы работаете с реальным девайсом). вы должны увидеть перелистывание подобное этому:

Конечно в качестве направления переворачивания листа в параметр setAnimationTransition  можноустанавливать значения UIViewAnimationTransitionFlipFromLeft и UIViewAnimationTransitionFlipFromRight но это уже будет просто поворот рисунка, а не перелистывание.
Пока конечно же рисунок остаётся тем же, что и был но мы можем вставить метод который менял бы что то например между строк
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:myimage cache:YES];
//добавить сюда выполнение метода
[UIView commitAnimations];
Тогда сможем реализовать подмену рисунка плавно, а пока на этом всё спасибо. что прочиталю статью.

Правильный разоворот изображения в IOS

Не так давно натолкнулся на проблему правильно повернуть картинку на нужный угол. Чаще всего это 90,180 градусов. Причем проблема заключалась не только в том, что бы развернуть картинку для пользователя на экране IPhone, а и сохранить изменения в графическом контексте, что бы запомнить эту трансформацию. Перебрав кучу методов я остановился на более простом и менее нагрузочном для системе методе. Данный способ не обрежет картинку не по сторонам ни по высоте или ширине, что немаловажно.

И так приступим. Подразумевается что у вас уже есть проект с размещенным на ViewVontroller-е вашей UIImageView куда вы будете показывать картинку до и полсе разворота. Поэтому я опишу лишь методы по работе с самим изображением.
Итак в методе viewDidLoad я подгружаю картинку из проекта
UIImage *img = [UIImage imageNamed:@"test.png"];
mainimage.image = img;
где mainimage это ваш UIImageView
Теперь релазуем два метода для переворота картинки вправо и влево:
-(void) RotateToLeft{
mainimage.image = [self imageByRotatingImage:img fromImageOrientation:UIImageOrientationLeft];
}
-(void)RotateToRight{
mainimage.image = [self imageByRotatingImage:img fromImageOrientation:UIImageOrientationRight];
}
Конечно вы можете написать эти методы как -(IBAction) и привязать вызов к кнопкам, но это не слишком сложно, что бы тратить на это время в этой статье :)
Итак мы видим, что в обоих случаях вызывается один и тот же метод -(UIImage*)imageByRotatingImage:(UIImage*)initImage fromImageOrientation:(UIImageOrientation)orientation только с разным параметром вконце. Он то как раз и указывает на ориентацию изображения в которую необходимо повернуть  картинку. Взял я его гдето на просторах интернетов и немножко окрасивил для понятного использования. Перед просмотром листинга кода метода хочу сказать, что перед его вызовом подразумевается, что текущая картинка имеет ориентацию UIImageOrientationUp.
Итак сам метод:
-(UIImage*)imageByRotatingImage:(UIImage*)initImage fromImageOrientation:(UIImageOrientation)orientation
{
CGImageRef imgRef = initImage.CGImage;
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGAffineTransform transform = CGAffineTransformIdentity;
CGRect bounds = CGRectMake(0, 0, width, height);
CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
CGFloat boundHeight;
UIImageOrientation orient = orientation;
switch(orient) {
case UIImageOrientationUp: //EXIF = 1
return initImage; //если прислать в качестве параметра orientation – UIImageOrientationUp метод вернёт саму картинку целой и невредмой
break;
case UIImageOrientationUpMirrored: //EXIF = 2  – Зеркальное отображение
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
break;
case UIImageOrientationDown: //EXIF = 3 – перевернет вертикально
transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationDownMirrored: //EXIF = 4 – Вертикальное зеркальное отображение
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
break;
case UIImageOrientationLeftMirrored: //EXIF = 5
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationLeft: //EXIF = 6 – Перевернуть влево -90 градусов
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationRightMirrored: //EXIF = 7
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeScale(-1.0, 1.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
case UIImageOrientationRight: //EXIF = 8 – Вправо тоесть +90 градусов по часовой стрелке
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
default:
[NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];
}
// Вот тут создается графический контекст
CGContextRef    context = NULL;
void *          bitmapData;
int             bitmapByteCount;
int             bitmapBytesPerRow;
bitmapBytesPerRow   = (bounds.size.width * 4);
bitmapByteCount     = (bitmapBytesPerRow * bounds.size.height);
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
return nil;
}
CGColorSpaceRef colorspace = CGImageGetColorSpace(imgRef);
context = CGBitmapContextCreate (bitmapData,bounds.size.width,bounds.size.height,8,bitmapBytesPerRow,
colorspace,kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorspace);
if (context == NULL)
// если неполучилось создать контекст по какой либо причине отдатим nil
return nil;
CGContextScaleCTM(context, -1.0, -1.0);
CGContextTranslateCTM(context, -bounds.size.width, -bounds.size.height);
CGContextConcatCTM(context, transform);
//Отрисовка изображения в графический контекст в памяти
CGContextDrawImage(context, CGRectMake(0,0,width, height), imgRef);
CGImageRef imgRef2 = CGBitmapContextCreateImage(context);
CGContextRelease(context);
free(bitmapData);
UIImage * image = [UIImage imageWithCGImage:imgRef2 scale:initImage.scale orientation:UIImageOrientationUp];
NSData *newimageData = UIImageJPEGRepresentation(image,1.0);
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:newimageData  forKey:@"chosed_image_data"];
CGImageRelease(imgRef2);
return image;
}
Во время выполнения этого метода может быть некая задержка в приложении, так как идёт выделение памяти и устройство в отличии от эмулятора может слегка притормозить свою активность, поэтому нужно быть готовым к этому. Итак мы имеем универсальный метод по развороту картинки на 90 и 180  градусов в любую сторону, а также возможность зеркально отобразить изображения. При этом сохраняя его в контексте. Чем это лучше чем просто трансформация в виде анимации? Потому, что после например анимированного траснформа поворота мы получим картинку повернутую на 90 градусов, но в контексте она останется в прежнем состоянии. И при повторном выполнении трансформации мы получим тот же результат – картинку развернутую на 90 градусов, а не на 180 как задумывалось.

Пропорциональное изменение размеров изображений в IOS IPhone

Статья посвящена теме об пропорциональном изменении размеров фотографий в вашем приложении для IPone IPad. Так как в большинстве приложений по работе  с фотографиями рано или поздно разработчик сталкивается с задачей уменьшить или увеличить размер фото согласно заданным величинам ширины или величины. И уменьшение необходимо сделать пропорционально например ширине как это зачастую бывает. Так как если просто накинуть пару сотен пикселов ширине или высоте картинки полученное изображение может иметь искажённый вид, например растянуто по высоте. Это может и сойдет если работа идет с объектами типа дерево или куст, но никак не удовлетворит заказчика если вы работаете с фотографиями людей. А также возможно вам просто необходимо уменьшить размеры фото что бы не тратить драгоценный мобильный трафик при передаче фотографии друзьям.

Итак сама реализация:
Как обычно предполагаю что у вас уже есть проект с созданным в storyboard UIImageView для работы с изображениями. viewDidLoad я подгружаю картинку из проекта
UIImage *img = [UIImage imageNamed:@"test.png"];
mainimage.image = img;
где mainimage это ваш UIImageView
Теперь напишем метод в который можно привязать к кнопке, по нажатию на котороую мы уменьшаем реальный размер фото пропорционально по ширине до 640px.
-(IBAction) scale:(id)sender{
// создаем объект класса CGSize что бы задать пропорции нового фото
// первый параметр это ширина второй высота, но играет роль только размер для ширины так как метод ориентирован именно на пропорциональное уменьшение/увеличение по ширине
CGSize sz = CGSizeMake(640, 640);
UIImage *img = [UIImage imageNamed:@"test.png"];
UIImage *newimage = [self ScaleImgPropoWidth:img scaledToSize:sz];
mainimage.image = newimage;
}
Как вы видите реализация скрыта в методе ScaleImgPropoWidth, давайте посмотрим на реализацию метода:
- (UIImage *)ScaleImgPropoWidth:(UIImage *)image scaledToSize:(CGSize)newSize {
double ratio;
double delta;
CGPoint offset;
//проверка на то если новый размер картинки больше или равен старому то вернуть ту же картинку
if(newSize.width>=image.size.width){
return image;
}//если данная проверка вам ненужна вы можете её убрать, напрмиер если хотите в любом случае кропить размер или же при увеличении размеров заменить условие на if(newSize.width<=image.size.width)
ratio = newSize.width / image.size.width;
delta = (ratio*image.size.height – ratio*image.size.width);
UIImage *scaledImage =
[UIImage imageWithCGImage:[image CGImage]
scale:(image.scale / ratio)
orientation:(image.imageOrientation)];
CGRect clipRect = CGRectMake(0, 0,
scaledImage.size.width,
scaledImage.size.height);
CGSize sz = CGSizeMake(newSize.width, scaledImage.size.height);
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
} else {
UIGraphicsBeginImageContext(sz);
}
UIRectClip(clipRect);
[image drawInRect:clipRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Как вы могли заметить преобразовании резамера происходит в графическом контексте, то есть это не просто трансформация изображения.

IOS Программируем жесты с UIGestureRecognizer

Все мы знаешь, что в приложениях для IOS есть возможность создавать события на основе жестов на экране IPhone или IPad. В XCode мы можем сделать это с помощью Storyboard или же описать их программно. На мой взгляд особой разницы нет как это делать, но в данной статье я напишу как делать это программно. Для начала разберемся с тем какие бывают жесты.
  • UITapGestureRecognizer   – жест нажатия пальцем на экран
  • UIPinchGestureRecognizer  - жест "ущипнуть" экран
  • UIRotationGestureRecognizer - жест вращения пальцами по экрану
  • UISwipeGestureRecognizer - жест "скольжения" то есть протянули пальцем
  • UIPanGestureRecognizer - жест, который выглядит как перетаскивание
  • UILongPressGestureRecognizer - долгое удерживание пальца на экране
Все эти жесты помогают нам так или иначе разнообразить работу и интерфейс приложения. Рассмотрим простой пример когда необходимо использовать жест нажатия например на изображение UIImageView. Поскольку IBOutlet UIImageView не имеет стандартной возможности воспринимать действия на себе  -  пример будет достаточно полезным.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOnImageView:)];
[image addGestureRecognizer:tap];
Tap- это объект распознователя жестов который я инициализировал с помощью указания себя в качестве передаваемого параметра в метод didTapOnImageView. Который отработает когда жест будет выполнен/или начат. Во второй строек я добавил его к объекту UIImageView image. Далее достаточно только создать метод didTapOnImageView как на примере:
-(void) didTapOnImageView:(UIGestureRecognizer*) recognizer {
//код который должен отработать
}
Почему я написал через слеш выполнен/или начат? Потому, что в методе  didTapOnImageView мы указали входящий параметр сам объект распознавания у которого можно получить его состояния и проверить их. Например таким условием:
if (recognizer.state == UIGestureRecognizerStateBegan)
//в данном случае проверка началось ли распознование жеста
При использовании UITapGestureRecognizer эта проверка встряли понадобится, но вот при UILongPressGestureRecognizer может быть к стати.
Список состояний которые можно проверить:
UIGestureRecognizerStatePossible - распознаватель еще не признал жест, но может быть сенсорным оценке событий. Это состояние по умолчанию.
UIGestureRecognizerStateFailed - распознаватель устанавливает этот статус когда жест не может быть выполнен

UIGestureRecognizerStateRecognized - распознаватель понял и распознал жест
UIGestureRecognizerStateBegan - распознавание жеста началось
UIGestureRecognizerStateChanged - распознаватель устанавливает этот статус когда жест был изменен и завершит распознавание на следующем цикле обработки

UIGestureRecognizerStateEnded  – распознавание жеста закончилось
UIGestureRecognizerStateCancelled - распознавание жеста было отменено

Манипулируя проверками этих состояний можно с точностью отловить, что происходит в тот или иной момент времени когда работает распознаватель жестов.
Рассмотрим ещё несколько опций которые могут быть использованы при работе с распознаванием жестов.  Например:
tap.minimumPressDuration = 1.0; //сколько секунд нужно удерживать палец на экране для распознавателя жеста UILongPressGestureRecognizer
[tap setNumberOfTouchesRequired:1]; //задает количество прикосновений которые необходимо сделать для распознавания жеста
Теперь рассмотрим еще один пример программной реализации жеста скольжения:
   UISwipeGestureRecognizer *swipetoleft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:@selector(didSwipe)];
[swipetoleft setDirection:UISwipeGestureRecognizerDirectionLeft];
[optpodlozhka addGestureRecognizer:swipetoleft];
В данном примере появилась ещё одна строка [swipetoleft setDirection:UISwipeGestureRecognizerDirectionLeft]; Которая указывает направление жеста. Это свойство характерно для UISwipeGestureRecognizer. Этим я хочу сказать что для каждого вида жеста могут быть использованы разные свойства и настройки. Полный список которых можно посмотреть в UIGestureRecognizer Class Reference.