Описание тега uiimage
А UIImage
object - это высокоуровневый способ отображения данных изображения. Вы можете создавать изображения из файлов, из объектов изображения Quartz или из необработанных данных изображения, которые вы получаете. ВUIImage
также предлагает несколько вариантов рисования изображений в текущем графическом контексте с использованием различных режимов наложения и значений непрозрачности. Вопросы, связанные с этим, можно пометить с помощью uiimage
Объекты изображений неизменяемы, поэтому вы не можете изменить их свойства после создания. Это означает, что вы обычно указываете свойства изображения во время инициализации или полагаетесь на метаданные изображения, чтобы предоставить значение свойства. Однако в некоторых случаяхUIImage
Класс предоставляет удобные методы для получения копии изображения, в котором используются настраиваемые значения свойства.
В ситуациях с нехваткой памяти данные изображения могут быть удалены из объекта UIImage, чтобы освободить память в системе. Это поведение очистки влияет только на данные изображения, хранящиеся внутри объекта UIImage, а не на сам объект. Когда вы пытаетесь нарисовать изображение, данные которого были очищены, объект изображения автоматически перезагружает данные из исходного файла. Однако этот дополнительный шаг загрузки может привести к небольшому снижению производительности.
Самый простой способ загрузить изображение - использовать метод imageNamed, однако он загружает данные в память и (как правило) не освобождает их, так что это идеально подходит только для небольших, часто используемых изображений.
UIImage *bgImage = [UIImage imageNamed:@"background.jpg"];
Использовать imageWithContentsOfFile
для чтения данных изображения из файла. Этот метод не кэширует изображение в памяти.
UIImage *bgImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"background" ofType:@"jpg"]]
Ресурсы:
Могу ли я получить исходный файл, представляющий этот образ на диске?
В iOS 4 можно, но это очень раздражает. Используйте следующий код, чтобы получить URL-адрес AssetsLibrary для изображения, а затем передайте URL-адрес в
assetForURL:resultBlock:failureBlock:
NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:referenceURL resultBlock:^(ALAsset *asset) {
// code to handle the asset here
} failureBlock:^(NSError *error) {
// error handling
}];
[library release];
Это раздражает, потому что пользователя спрашивают, может ли ваше приложение получить доступ к вашему текущему местоположению, что довольно сбивает с толку, поскольку вы фактически пытаетесь получить доступ к библиотеке фотографий пользователя. Если вы на самом деле не пытаетесь получить данные о местоположении EXIF, пользователь будет немного сбит с толку.
Обязательно включите фреймворк AssetsLibrary, чтобы эта работа работала.
Как мне посмотреть на пиксели, лежащие в основе UIImage
?
Поскольку UIImage
неизменяем, вы не можете смотреть на прямые пиксели. Однако вы можете сделать копию. Код этого выглядит примерно так:
UIImage* image = ...; // An image
NSData* pixelData = (NSData*) CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
unsigned char* pixelBytes = (unsigned char *)[pixelData bytes];
// Take away the red pixel, assuming 32-bit RGBA
for(int i = 0; i < [pixelData length]; i += 4) {
pixelBytes[i] = 0; // red
pixelBytes[i+1] = pixelBytes[i+1]; // green
pixelBytes[i+2] = pixelBytes[i+2]; // blue
pixelBytes[i+3] = pixelBytes[i+3]; // alpha
}
Однако обратите внимание, что CGDataProviderCopyData
предоставляет вам "неизменяемую" ссылку на данные - это означает, что вы не можете их изменить (и вы можете получить BAD_ACCESS
ошибка, если вы это сделаете). Посмотрите на следующий вопрос, если хотите увидеть, как можно изменить пиксели.
Как изменить пиксели UIImage
?
В UIImage
неизменен, то есть вы не можете его изменить. Apple опубликовала отличную статью о том, как получить копию пикселей и изменить их, и вместо того, чтобы копировать и вставлять ее сюда, вам нужно просто прочитать статью.
Когда у вас есть контекст растрового изображения, как они упоминаются в статье, вы можете сделать что-то подобное, чтобы получить новый UIImage
с измененными пикселями:
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage* newImage = [UIImage imageWithCGImage:ref];
Не забудьте выпустить свои ссылки, иначе вы потеряете довольно много памяти.
Как изменить размер изображения?
К сожалению, нет определенного способа изменить размер изображения. Также важно отметить, что при изменении размера вы получите новое изображение - вы не изменяете старое.
Есть несколько способов изменить размер. Я представлю их здесь и объясню плюсы и минусы каждого из них.
Метод 1: использование UIKit
+ (UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
// Create a graphics image context
UIGraphicsBeginImageContext(newSize);
// Tell the old image to draw in this new context, with the desired
// new size
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// End the context
UIGraphicsEndImageContext();
// Return the new image.
return newImage;
}
Этот метод очень прост и отлично работает. Он также будет иметь дело с UIImageOrientation для вас, что означает, что вам не нужно заботиться о том, была ли камера сбоку, когда был сделан снимок. Однако этот метод не является потокобезопасным, и поскольку создание миниатюр - относительно дорогостоящая операция (примерно ~2,5 с в сети 3G для1600 x 1200
пиксельное изображение), это в значительной степени операция, которую вы можете выполнять в фоновом режиме в отдельном потоке.
Метод 2: Использование CoreGraphics
+ (UIImage*)imageWithImage:(UIImage*)sourceImage scaledToSize:(CGSize)newSize;
{
CGFloat targetWidth = newSize.width;
CGFloat targetHeight = newSize.height;
CGImageRef imageRef = [sourceImage CGImage];
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);
if (bitmapInfo == kCGImageAlphaNone) {
bitmapInfo = kCGImageAlphaNoneSkipLast;
}
CGContextRef bitmap;
if (sourceImage.imageOrientation == UIImageOrientationUp || sourceImage.imageOrientation == UIImageOrientationDown) {
bitmap = CGBitmapContextCreate(NULL, targetWidth, targetHeight, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
} else {
bitmap = CGBitmapContextCreate(NULL, targetHeight, targetWidth, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
}
if (sourceImage.imageOrientation == UIImageOrientationLeft) {
CGContextRotateCTM (bitmap, M_PI_2); // + 90 degrees
CGContextTranslateCTM (bitmap, 0, -targetHeight);
} else if (sourceImage.imageOrientation == UIImageOrientationRight) {
CGContextRotateCTM (bitmap, -M_PI_2); // - 90 degrees
CGContextTranslateCTM (bitmap, -targetWidth, 0);
} else if (sourceImage.imageOrientation == UIImageOrientationUp) {
// NOTHING
} else if (sourceImage.imageOrientation == UIImageOrientationDown) {
CGContextTranslateCTM (bitmap, targetWidth, targetHeight);
CGContextRotateCTM (bitmap, -M_PI); // - 180 degrees
}
CGContextDrawImage(bitmap, CGRectMake(0, 0, targetWidth, targetHeight), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage* newImage = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return newImage;
}
Преимущество этого метода состоит в том, что он является потокобезопасным, плюс он заботится обо всех мелочах (используя правильное цветовое пространство и информацию о растровом изображении, имея дело с ориентацией изображения), что делает версия UIKit.
Как изменить размер и сохранить соотношение сторон (например, параметр AspectFill)?
Он очень похож на метод, описанный выше, и выглядит так:
+ (UIImage*)imageWithImage:(UIImage*)sourceImage scaledToSizeWithSameAspectRatio:(CGSize)targetSize;
{
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor) {
scaleFactor = widthFactor; // scale to fit height
}
else {
scaleFactor = heightFactor; // scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor) {
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}
else if (widthFactor < heightFactor) {
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
CGImageRef imageRef = [sourceImage CGImage];
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);
if (bitmapInfo == kCGImageAlphaNone) {
bitmapInfo = kCGImageAlphaNoneSkipLast;
}
CGContextRef bitmap;
if (sourceImage.imageOrientation == UIImageOrientationUp || sourceImage.imageOrientation == UIImageOrientationDown) {
bitmap = CGBitmapContextCreate(NULL, targetWidth, targetHeight, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
} else {
bitmap = CGBitmapContextCreate(NULL, targetHeight, targetWidth, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
}
// In the right or left cases, we need to switch scaledWidth and scaledHeight,
// and also the thumbnail point
if (sourceImage.imageOrientation == UIImageOrientationLeft) {
thumbnailPoint = CGPointMake(thumbnailPoint.y, thumbnailPoint.x);
CGFloat oldScaledWidth = scaledWidth;
scaledWidth = scaledHeight;
scaledHeight = oldScaledWidth;
CGContextRotateCTM (bitmap, M_PI_2); // + 90 degrees
CGContextTranslateCTM (bitmap, 0, -targetHeight);
} else if (sourceImage.imageOrientation == UIImageOrientationRight) {
thumbnailPoint = CGPointMake(thumbnailPoint.y, thumbnailPoint.x);
CGFloat oldScaledWidth = scaledWidth;
scaledWidth = scaledHeight;
scaledHeight = oldScaledWidth;
CGContextRotateCTM (bitmap, -M_PI_2); // - 90 degrees
CGContextTranslateCTM (bitmap, -targetWidth, 0);
} else if (sourceImage.imageOrientation == UIImageOrientationUp) {
// NOTHING
} else if (sourceImage.imageOrientation == UIImageOrientationDown) {
CGContextTranslateCTM (bitmap, targetWidth, targetHeight);
CGContextRotateCTM (bitmap, -M_PI); // - 180 degrees
}
CGContextDrawImage(bitmap, CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledWidth, scaledHeight), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage* newImage = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return newImage;
}
Используемый здесь метод состоит в том, чтобы создать растровое изображение желаемого размера, но нарисовать изображение, которое на самом деле больше, таким образом сохраняя соотношение сторон.
Итак, у нас есть масштабированные образы - как сохранить их на диск?
Это довольно просто. Помните, что мы хотим сохранить на диск сжатую версию, а не несжатые пиксели. Apple предоставляет две функции, которые помогают нам в этом (документация здесь):
NSData* UIImagePNGRepresentation(UIImage *image);
NSData* UIImageJPEGRepresentation (UIImage *image, CGFloat compressionQuality);
И если вы хотите их использовать, сделайте что-то вроде:
UIImage* myThumbnail = ...; // Get some image
NSData* imageData = UIImagePNGRepresentation(myThumbnail);
Теперь мы готовы сохранить его на диск, что является последним шагом (скажем, в каталог документов):
// Give a name to the file
NSString* imageName = @"MyImage.png";
// Now, we have to find the documents directory so we can save it
// Note that you might want to save it elsewhere, like the cache directory,
// or something similar.
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
// Now we get the full path to the file
NSString* fullPathToFile = [documentsDirectory stringByAppendingPathComponent:imageName];
// and then we write it out
[imageData writeToFile:fullPathToFile atomically:NO];
Вы должны повторить это для каждой версии изображения, которое у вас есть.
Как мне загрузить эти изображения обратно в память?
Вы только посмотрите на различные UIImage
методы инициализации, такие как +imageWithContentsOfFile:
в документации Apple.
Информация получена из теперь удаленного сообщения о переполнении стека: UIImagePickerController, UIImage, Memory и др.?