Квадратный миниатюра изображения UIImagePickerViewController

В моем приложении пользователь выбирает изображение или снимает изображение с помощью UIImagePickerViewController. Как только изображение было выбрано, я хочу отобразить его миниатюру на квадрате UIImageView (90x90).

Я использую код Apple для создания миниатюры. Проблема заключается в том, что миниатюра не квадратизирована, функция после того, как ключ kCGImageSourceThumbnailMaxPixelSize задал значение 90, кажется, только для изменения высоты изображения, и насколько я знаю, ключ kCGImageSourceThumbnailMaxPixelSize должен отвечать за настройку высоты и ширины миниатюры.

Вот мой код:

- (void)imagePickerController:(UIImagePickerController *)picker
    didFinishPickingMediaWithInfo:(NSDictionary *)info {

    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];

    NSData *imageData = UIImageJPEGRepresentation (image, 0.5);

    // My image view is 90x90
    UIImage *thumbImage = MyCreateThumbnailImageFromData(imageData, 90);

    [myImageView setImage:thumbImage];

    if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {

        UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    }

    [picker dismissViewControllerAnimated:YES completion:nil];
}

UIImage* MyCreateThumbnailImageFromData (NSData * data, int imageSize) {

    CGImageRef        myThumbnailImage = NULL;
    CGImageSourceRef  myImageSource;
    CFDictionaryRef   myOptions = NULL;
    CFStringRef       myKeys[3];
    CFTypeRef         myValues[3];
    CFNumberRef       thumbnailSize;

    // Create an image source from NSData; no options.
    myImageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data,
                                                NULL);

    // Make sure the image source exists before continuing.
    if (myImageSource == NULL){
        fprintf(stderr, "Image source is NULL.");
        return  NULL;
    }

    // Package the integer as a  CFNumber object. Using CFTypes allows you
    // to more easily create the options dictionary later.
    thumbnailSize = CFNumberCreate(NULL, kCFNumberIntType, &imageSize);

    // Set up the thumbnail options.
    myKeys[0] = kCGImageSourceCreateThumbnailWithTransform;
    myValues[0] = (CFTypeRef)kCFBooleanTrue;
    myKeys[1] = kCGImageSourceCreateThumbnailFromImageIfAbsent;
    myValues[1] = (CFTypeRef)kCFBooleanTrue;
    myKeys[2] = kCGImageSourceThumbnailMaxPixelSize;
    myValues[2] = thumbnailSize;

    myOptions = CFDictionaryCreate(NULL, (const void **) myKeys,
                                   (const void **) myValues, 2,
                                   &kCFTypeDictionaryKeyCallBacks,
                                   & kCFTypeDictionaryValueCallBacks);

    // Create the thumbnail image using the specified options.
    myThumbnailImage = CGImageSourceCreateThumbnailAtIndex(myImageSource,
                                                           0,
                                                           myOptions);

    UIImage* scaled = [UIImage imageWithCGImage:myThumbnailImage];

    // Release the options dictionary and the image source
    // when you no longer need them.

    CFRelease(thumbnailSize);
    CFRelease(myOptions);
    CFRelease(myImageSource);

    // Make sure the thumbnail image exists before continuing.
    if (myThumbnailImage == NULL) {
        fprintf(stderr, "Thumbnail image not created from image source.");
        return NULL;
    }
    return scaled;
}

И вот как создается мое изображение:

myImageView = [[UIImageView alloc] init];
imageView.contentMode = UIViewContentModeScaleAspectFit;

CGRect rect = imageView.frame;
rect.size.height = 90;
rect.size.width = 90;

imageView.frame = rect;
[imageView setUserInteractionEnabled:YES];

Если я не установил imageView.contentMode = UIViewContentModeScaleAspectFit;, миниатюра будет искажена, так как это просто версия моего исходного изображения с высотой 90 пикселей.

Итак, почему мой миниатюра не квадрат?

Ответы

Ответ 1

Проще всего было бы установить contentMode на вашем изображенииView UIViewContentModeScaleAspectFill. Однако это может быть не идеальным, поскольку он сохранит весь образ в памяти.

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

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size
{
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

Эта версия будет искажать изображение, если размер не совпадает с форматом изображения.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToFillSize:(CGSize)size
{
    CGFloat scale = MAX(size.width/image.size.width, size.height/image.size.height);
    CGFloat width = image.size.width * scale;
    CGFloat height = image.size.height * scale;
    CGRect imageRect = CGRectMake((size.width - width)/2.0f,
                                  (size.height - height)/2.0f,
                                  width,
                                  height);

    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [image drawInRect:imageRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

Часто требуется только верхний квадрат изображения (а не центра). И вы хотите, чтобы конечное изображение имело определенный размер, скажем 128x128, как в примере ниже.

- (UIImage *)squareAndSmall // as a category (so, 'self' is the input image)
{
    // fromCleverError original
    // http://stackoverflow.com/questions/17884555
    CGSize finalsize = CGSizeMake(128,128);

    CGFloat scale = MAX(
        finalsize.width/self.size.width,
        finalsize.height/self.size.height);
    CGFloat width = self.size.width * scale;
    CGFloat height = self.size.height * scale;

    CGRect rr = CGRectMake( 0, 0, width, height);

    UIGraphicsBeginImageContextWithOptions(finalsize, NO, 0);
    [self drawInRect:rr];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

Ответ 2

Вот ссылка, на которую вы можете ссылаться:

1) UIImage + Resize

2) UIImage + ProportionalFill

Здесь вы можете изменить размер представления, а также добавить пропорциональную заливку, чтобы пропорционально соотношение сторон и другие функции, такие как обрезка, масштаб и т.д.

Вы можете использовать метод -(UIImage*)resizedImageToSize:(CGSize*)size в ссылочной ссылке UIImage + Resize выше только для повторного создания изображения.

Надеюсь, это поможет вам.

Сообщите мне, если вам нужна дополнительная помощь.

Ответ 3

Свифт 2.x версии умной ошибки выше:

    // Define thumbnail size
    let size = CGSize(width: 100, height: 100)

    // Define rect for thumbnail
    let scale = max(size.width/image.size.width, size.height/image.size.height)
    let width = image.size.width * scale
    let height = image.size.height * scale
    let x = (size.width - width) / CGFloat(2)
    let y = (size.height - height) / CGFloat(2)
    let thumbnailRect = CGRectMake(x, y, width, height)

    // Generate thumbnail from image
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    image.drawInRect(thumbnailRect)
    let thumbnail = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

Ответ 4

Другой способ сделать это - столкнулся с проблемой на днях, и другие люди могут иметь такую ​​же проблему при использовании кода примера Apple, который лучше всего подходит для больших изображений.

Как уже упоминалось в принятом ответе, проблема загрузки всего изображения в память и iOS может убить ваше приложение, если оно слишком велико. CGImageSourceCreateThumbnailAtIndex в примере кода Apple более эффективен, однако в этом примере есть ошибка. Третий параметр в CFDictionaryCreate - это "numValues" для копирования. Требуется 3 не 2.

myOptions = CFDictionaryCreate(NULL, (const void **) myKeys,  
                               (const void **) myValues, 
                               3, //Changed to 3  
                               &kCFTypeDictionaryKeyCallBacks,  
                               & kCFTypeDictionaryValueCallBacks);