2013-07-26 10 views
14

W mojej aplikacji użytkownik wybiera obraz lub robi zdjęcie za pomocą UIImagePickerViewController. Po wybraniu obrazu chcę wyświetlić jego miniaturę na kwadratowym UIImageView (90x90).Kwadratowa miniatura z UIImagePickerViewController image

Używam Apple's code do utworzenia miniatury. Problem polega na tym, że miniaturka nie jest podniesiona do kwadratu, funkcja, po ustawieniu klawisza kCGImageSourceThumbnailMaxPixelSize na 90, wydaje się zmieniać rozmiar obrazu, i o ile wiem, klucz kCGImageSourceThumbnailMaxPixelSize powinien być odpowiedzialny za ustawienie wysokości i szerokości miniatury.

Oto rzut oka mojego kodu:

- (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; 
} 

A to, jak moim zdaniem obraz jest tworzony:

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]; 

Jeśli nie ustawić imageView.contentMode = UIViewContentModeScaleAspectFit; miniaturka będzie zniekształcony, ponieważ to tylko wersja mojego oryginalnego obrazu o wysokości 90 pikseli.

Dlaczego moja miniatura nie jest kwadratowa?

+0

Uwaga - Meta użytkownik bez wiedzy iOS edytowany dala jakiś super-poręczny kod od odpowiedzi Clever'a poniżej. jeśli faktycznie programujesz i faktycznie potrzebujesz przydatnej odpowiedzi i rzeczywiście chcesz użyć sprytnych pomysłów Clever, po prostu kliknij przycisk "edytowany" poniżej, zobaczysz historię i możesz wycinać, wklejać i wysyłać do sklepu z aplikacjami:) – Fattie

+0

@JoeBlow Nie widzę żadnego usuniętego kodu w [edit history] (http://stackoverflow.com/posts/17884863/revisions) odpowiedzi Clever Error. – cpburnz

+0

http://stackoverflow.com/posts/17884863/revisions rev "5" – Fattie

Odpowiedz

52

Najprostszym rozwiązaniem jest ustawienie contentMode na obrazie, aby zamiast niego był UIViewContentModeScaleAspectFill. Może to jednak nie być idealne, ponieważ zachowałoby cały obraz w pamięci.

Oto kod, którego używam do zmiany rozmiaru zdjęć.

+ (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; 
} 

Ta wersja spowoduje, że obraz nie będzie zniekształcony, jeśli jego rozmiar nie jest taki sam jak obraz.

+ (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; 
} 

często chcesz tylko górny kwadrat obrazu (zamiast centrum). A chcesz, aby ostateczny obraz miał pewną wielkość, na przykład 128x128, jak w poniższym przykładzie.

- (UIImage *)squareAndSmall // as a category (so, 'self' is the input image) 
{ 
    // fromCleverError's 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; 
} 
+0

To podejście zniekształca obraz, próbowałem już czegoś takiego. – douglasd3

+2

Dodałem wersję, która nie powinna zniekształcać obrazu. –

+0

Teraz nie zniekształca, ale obraz miniatury nie jest podniesiony do kwadratu, to znaczy, jest 90x90, ale obraz nie został przeskalowany, aby wypełnić kwadrat. – douglasd3

6

Oto link można znaleźć na stronie:

1) UIImage+Resize

2) UIImage+ProportionalFill

Tutaj można zmienić rozmiar widoku, a także dodać proporcjonalny do syta tak że proporcjonalnie proporcje i inne funkcje takie jak kadrowanie, skalowanie itp.

Możesz użyć metody -(UIImage*)resizedImageToSize:(CGSize*)size pod odnośnikiem UIImage + Zmień rozmiar powyżej, aby zmienić rozmiar obrazu.

Mam nadzieję, że to pomoże.

Daj mi znać, jeśli potrzebujesz dodatkowej pomocy.

+0

Drugi link był naprawdę pomocny! Nadal nie wiem, na czym polegał problem, ale teraz działa i zaoszczędzi mi to wiele czasu. Dzięki! – douglasd3

+1

@ douglasd3: Drugi link jest bardzo pomocny przy wielu funkcjach, takich jak kadrowanie i skalowanie. –

0

Innym sposobem, aby to zrobić - wpadł emisji na drugi dzień i inni ludzie mogą mieć ten sam problem przy korzystaniu z apple sample code który jest najlepszy dla dużych obrazów.

Jak wspomniano w przyjętej odpowiedzi, ma to problem z załadowaniem całego obrazu do pamięci, a iOS może zabić aplikację, jeśli jest zbyt duża.CGImageSourceCreateThumbnailAtIndex w przykładowym kodzie Apple'a jest bardziej wydajny, jednak w tym przykładzie występuje błąd. Trzeci parametr w CFDictionaryCreate to "numValues" do skopiowania. Musi być nie 2.

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

ok, ale przetestowałem tę metodę przed akceptacją odpowiedzi za pomocą kwarcu, a zaakceptowana odpowiedź jest 15 razy szybsza, szczególnie jeśli już masz 'UIImage', ponieważ musisz ją przekonwertować na' CFData', abyś mógł użyć 'CGImageSourceCreateThumbnailAtIndex' . Cały ten proces jest 15 razy wolniejszy. – SpaceDog

3

Swift w wersji 2.x odpowiedzi Clever Błąd w powyższym:

// 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() 
+1

Dla Swift 3 zmień przypisanie let noteRegulacja na CGRect (x: x, y: y, width: width, height: height) –