Сохранение содержимого UIView в iOS 4 с реальным размером изображений внутри (то есть масштабирование контента для сохранения)

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

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

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

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

Ответ 1

Реализация для рендеринга любого 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.

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

Ответ 2

Я выполнил такую ​​вещь, чтобы сохранить булавки из MKMapView в виде PNG файла (на экране сетчатки): MKPinAnnotationView: есть ли более трех цветов?

Здесь выдержка из важной части, которая выполняет сохранение UIView ( theView) с использованием определения сетчатки:

 
- (void) saveMyView: (UIView *) theView {   // Изображение, в котором будет сохранен контент представления.   UIImage * image = nil;   UIGraphicsBeginImageContextWithOptions (theView.frame.size, NO, 2.0);   [theView.layer renderInContext: UIGraphicsGetCurrentContext()];   image = UIGraphicsGetImageFromCurrentImageContext();   UIGraphicsEndImageContext();   NSData * imgData = UIImagePNGRпредставление (изображение);   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; }

Ответ 3

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

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

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

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

Ответ 4

Не могли бы вы просто создать новый графический контекст с нужным размером, использовать 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();

Ответ 5

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

#import <QuartzCore/QuartzCore.h>

Мои изображения - это свойства, если это не так, игнорируйте .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;
}