Salvando o conteúdo da UIView no iOS 4 com o tamanho real das imagens dentro (ou seja, conteúdo de escala para salvar)

StackOverflow https://stackoverflow.com/questions/4213529

Pergunta

Eu tenho um UIView com muitas visualizações de UIImage como Subviews. O aplicativo é executado no iOS4 e eu uso imagens com resolução de exibição Retina (ou seja, as imagens carregam com escala = 2)

Quero salvar o conteúdo da UIView ... mas ... tenha o tamanho real das imagens dentro. Ou seja, a visualização possui tamanho 200x200 e imagens com escala = 2 dentro, eu gostaria de salvar uma imagem resultante de 400x400 e todas as imagens com seu tamanho real.

Agora, o que vem primeiro à mente é criar um novo contexto de imagem e carregar novamente todas as imagens dentro com escala = 1 e isso deveria fazer, mas eu queria saber se existe alguma maneira mais elegante de fazer isso? Parece uma cintura de memória e tempo do processador para recarregar tudo de novo, já que já foi feito ...

PS se alguém tiver uma resposta - incluindo código seria bom

Foi útil?

Solução

Implementação para renderização qualquer uiview para a imagem (trabalhando também para exibição de retina).

Helper.h Arquivo:

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

e implementação pertencente no arquivo 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 é o fator de escala. O fator de escala a ser aplicado ao bitmap. Se você especificar um valor de 0,0, o fator de escala será definido como o fator de escala da tela principal do dispositivo.

Quartzcore.framework Também deve ser colocado no projeto, porque estamos chamando a função no objeto da camada.

Para ativar o link fraco na estrutura do UIKIT, clique no item do projeto no navegador esquerdo, clique no alvo do projeto -> Fases de construção -> Link binário e escolha "opcional" (fraco) tipo na estrutura do UIKIT.

Aqui está biblioteca com extensões semelhantes para uicolor, uiimage, nsarray, nsdictionary, ...

Outras dicas

Eu realizei tal coisa para salvar os pinos do mkmapview como um arquivo png (na tela retina): MkpinannotationView: Existem mais de três cores disponíveis?

Aqui está um extrato da parte crucial que executa a economia de um UIView (theView) Usando sua definição de retina:


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

A chave é que o terceiro parâmetro para uigraphicsBeginImageContextWithOptions é o escala, que determina como a imagem será finalmente escrita.

Se você sempre deseja as dimensões de pixels reais, use [[UISCEREEN Mainscreen] Scale] para obter a escala atual da tela:

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

Se você usar a escala = 1.0 no iPhone4, você terá uma imagem com sua dimensão em pontos e o resultado é reduzido da verdadeira contagem de pixels. Se você escrever manualmente a imagem em uma dimensão de 640x960 (por exemplo: passando pixels como o primeiro parâmetro), na verdade será a imagem reduzida que é ampliada, que parece tão terrível quanto você imaginaria.

Você não poderia apenas criar um novo contexto de gráficos no tamanho desejado, usar um CGAFFINETRANSFORM para escalá -lo, renderizar a camada raiz da raiz uiview, restaurar o contexto ao tamanho original e renderizar a imagem? Não tentei isso para o conteúdo da retina, mas isso parece funcionar bem para imagens grandes que foram reduzidas no UIImageViews ...

algo como:

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

Importar quartzcore (clique em Projeto Principal, Construir Fases, Importar) e onde você precisa adicionar:

#import <QuartzCore/QuartzCore.h>

Minhas visualizações de imagem são propriedades, se você não for, ignore o .self e passe as visualizações de imagem para a função como parâmetros, depois ligue renderInContext nas duas imagens em um novo 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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top