2010-11-18 6 views
10

У меня есть UIView со многими UIImageViews в качестве подзонов. Приложение работает на iOS4, и я использую изображения с разрешением экрана сетчатки (т.е. загрузка изображений со шкалой = 2)Сохранение содержимого UIView в iOS 4 с реальным размером изображений внутри (то есть масштабирование содержимого для сохранения)

Я хочу сохранить содержимое UIView ... НО ... имеют реальный размер изображений внутри. То есть вид имеет размер 200x200 и изображения со шкалой = 2 внутри, я хотел бы сохранить результирующее изображение 400x400 и все изображения с их реальным размером.

Теперь, прежде всего, нужно создать новый контекст изображения и загрузить все изображения внутри с помощью scale = 1, и это должно было бы сделать, но мне было интересно, есть ли еще более элегантный способ сделать это? Кажется, что талия памяти и процессорное время перезаряжают все снова, так как это уже сделано ...

p.s. если у кого-то есть ответ - в том числе код будет приятным

ответ

25

Реализация для рендеринга любой UIView к изображению (работает также для отображения сетчатки).

helper.h файл:

@interface UIView (Ext) 
- (UIImage*) renderToImage; 
@end 

и принадлежность реализации в helper.m файл:

#import <QuartzCore/QuartzCore.h> 

@implementation UIView (Ext) 
- (UIImage*) renderToImage 
{ 
    // IMPORTANT: using weak link on UIKit 
    if(UIGraphicsBeginImageContextWithOptions != NULL) 
    { 
    UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0); 
    } else { 
    UIGraphicsBeginImageContext(self.frame.size); 
    } 

    [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return image; 
} 

0,0 является масштабным коэффициентом. Масштабный коэффициент для применения к растровому изображению. Если вы укажете значение 0.0, масштабный коэффициент будет установлен на масштабный коэффициент главного экрана устройства.

QuartzCore.framework также должен быть помещен в проект, потому что мы вызываем функцию на объект слоя.

Чтобы включить слабую ссылку в каркасе UIKit, щелкните элемент проекта в левом навигаторе, щелкните по объекту проекта -> фазы сборки -> ссылка на двоичный код и выберите «необязательный» (слабый) тип в среде UIKit.

library Вот с подобными расширениями для UIColor, UIImage, NSArray, NSDictionary, ...

+0

Какой смысл проверять, можете ли вы использовать 'UIGraphicsBeginImageContextWithOptions'? Это для обратной совместимости? – pixelfreak

+0

Это решение также требует рамки QuartzCore для метода renderInContext. – arlomedia

+0

@pixelfreak это потому, что UIGraphicsBeginImageContextWithOptions доступен только на iOS 4 +. – shannoga

0

Не могли бы вы просто создать новый графический контекст с требуемым размером, использовать CGAffineTransform, чтобы уменьшить масштаб, отобразить корневой слой корневого UIView, восстановить контекст оригинальный размер и сделать изображение? Не пробовал это на сетчатку содержания, но это, кажется, работает хорошо для больших изображений, которые были сокращены в UIImageViews ...

что-то вроде:

CGSize originalSize = myOriginalImage.size; //or whatever 

//create context 
UIGraphicsBeginImageContext(originalSize); 
CGContextRef context = UIGraphicsGetCurrentContext(); 
CGContextSaveGState(context); //1 original context 

// translate/flip the graphics context (for transforming from CG* coords to UI* coords 
CGContextTranslateCTM(context, 0, originalSize.height); 
CGContextScaleCTM(context, 1.0, -1.0); 

//original image 
CGContextDrawImage(context, CGRectMake(0,0,originalSize.width,originalSize.height), myOriginalImage.CGImage); 

CGContextRestoreGState(context);//1 restore to original for UIView render; 

//scaling 
CGFloat wratio = originalSize.width/self.view.frame.size.width; 
CGFloat hratio = originalSize.height/self.view.frame.size.height; 

//scale context to match view size 
CGContextSaveGState(context); //1 pre-scaled size 
CGContextScaleCTM(context, wratio, hratio); 

//render 
[self.view.layer renderInContext:context]; 

CGContextRestoreGState(context);//1 restore to pre-scaled size; 

UIImage *exportImage = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 
+0

Привет Стивен, поправьте меня, если я ошибаюсь, но это не то, что на самом деле имея уменьшенная визуализации (т.е. с использованием 1/4 пикселя данные изображения), которые не увеличены, или ядро графика работает по-другому? –

+0

Я мог ошибаться здесь, потому что я немного нечеткий по специфике этой части кварца, но при тестировании с помощью выше показано, что это не уменьшенный рендер. Сначала я специально создавал уменьшенный контекст растрового изображения, масштабировался и визуализировался с использованием контекста растрового изображения, но результатом было дерьмо (как и следовало ожидать). Однако вышеупомянутый метод обеспечивает четкое изображение, если исходные источники имеют разумный размер/качество. –

+0

В качестве последующего слова этот вышеприведенный код предполагает, что myOriginalImage больше, чем размер точки UIView (сетчатка или иначе) –

4

Я проводил такую ​​вещь, чтобы сохранить штифты из MKMapView в виде файла PNG (в сетчатке дисплей): MKPinAnnotationView: Are there more than three colors available?

Вот выдержка из решающей части, которая выполняет сохранение в UIView (theView), используя его определение сетчатки глаза:

 
-(void) saveMyView:(UIView*)theView { 
    //The image where the view content is going to be saved. 
    UIImage* image = nil; 
    UIGraphicsBeginImageContextWithOptions(theView.frame.size, NO, 2.0); 
    [theView.layer renderInContext: UIGraphicsGetCurrentContext()]; 
    image = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    NSData* imgData = UIImagePNGRepresentation(image); 
    NSString* targetPath = [NSString stringWithFormat:@"%@/%@", [self writablePath], @"thisismyview.png" ]; 
    [imgData writeToFile:targetPath atomically:YES]; 
}

-(NSString*) writablePath { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; return documentsDirectory; }

+0

Привет, +1 за упоминание UIGraphicsBeginImageContextWithOptions ... черт возьми, есть тонна новых случайных функций, таких как выход с каждым незначительным обновлением iOS ... Я устаю :) –

+0

UIGraphicsBeginImageContextWithOptions тоже для меня тоже. Следует внимательно изучить обновления ... –

+0

спасибо за подсказку! –

2

Ключ в том, что третий параметр UIGraphicsBeginImageContextWithOptions является шкала, который определяет, как будет в конечном итоге записать образ вне.

Если вы хотите всегда реальные размеры в пикселях, используйте [[UIScreen В начало] шкала], чтобы получить текущий масштаб экрана:

UIGraphicsBeginImageContextWithOptions(viewSizeInPoints, YES, [[UIScreen mainScreen] scale]);

При использовании шкалы = 1.0 на iPhone4, вы будете получите изображение с его размером в точках, и результат будет уменьшен с истинного количества пикселей. Если вы вручную выведете изображение на размер 640x960 (например, передаете пиксели в качестве первого параметра), это будет фактически уменьшенное изображение, которое масштабируется, что выглядит так же ужасно, как вы думаете, что это будет выглядеть.

+1

В соответствии с документами установка аргумента масштаба 0.0 означает то же самое, что и установка [[UIScreen mainScreen]]. – arlomedia

0

Импорт QuartzCore (Нажмите главный проект, Build Phases, импорт) и где вам это нужно добавить:

#import <QuartzCore/QuartzCore.h> 

Мои imageViews являются свойства, если ваш не игнорируйте .self и передать imageViews в функцию как параметры, а затем вызвать renderInContext на двух изображениях в новом UIGraphicsCurrentContext

- (UIImage *)saveImage 
{ 
    UIGraphicsBeginImageContextWithOptions(self.mainImage.bounds.size, NO, 0.0); 

    [self.backgroundImage.layer renderInContext:UIGraphicsGetCurrentContext()]; 
    [self.mainImage.layer renderInContext:UIGraphicsGetCurrentContext()]; 

    UIImage *savedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return savedImage; 
}