30 Dez, 2011 15:46

Utilizando imagens redimensionáveis no iOS

Solução trivial

Se, inocentemente, você simplesmente redimensionar a UIImageView para o formato desejado, a imagem ficará distorcida como nos exemplos abaixo.

UIImage redimensionada de modo errado

A segunda opção, e provavelmente a escolhida pela maioria, é criar uma imagem para cada cenário onde será utilizada, chegando aos resultados abaixo.

Uma UIImage para cada cenário

Essa solução atinge o objetivo visual esperado, mas dificulta a gerência de diversos arquivos muito semelhantes e ainda aumenta o tamanho do aplicativo final.

Soluções com apenas uma imagem

Como vimos anteriormente, o Android SDK possui suporte nativo ao conceito de 9-patch o que resolve facilmente o problema acima a partir de um único arquivo .9.png. No iOS não existe suporte nativo ao formato, mas existem soluções menos flexíveis que podem ser empregadas para atingir o mesmo objetivo.

UIImage strechable/resizable

Se seu aplicativo precisa funcionar em iOS 4.x ou inferior:

- (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight;

O método acima cria uma imagem a partir da original informando a porção desta (em pixels) que deve permanecer inalterada quando houver redimensionamento. Conceitualmente, o resultado fica assim:

Conceito do UIImage stretchableImageWithLeftCapWidth:topCapHeight:

O retângulo vermelho será preenchido pelo pixel na posição (leftCapWidth + 1, topCapHeight + 1), enquanto as regiões amarelas serão preenchidas pelo segmento lateral de 1px no sentido indicado pelas setas.

Ex:

UIImage *image = [[UIImage imageNamed:@"stretch.png"] 
                  stretchableImageWithLeftCapWidth:13 topCapHeight:15];

self.imageView.image = image;

O método acima está obsoleto no iOS 5. Neste caso, você poderá usar:

- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets;

Assim, como o método antigo, este cria uma imagem a partir da original com definições de como esta deve ser redimensionada. A diferença é que utilizamos um UIEdgeInsets para definir um retângulo que servirá de referência para o processo de redimensionamento. Eis o conceito:

Conceito do UIImage resizableImageWithCapInsets:

Agora, ao invés de expandir um único pixel, será definida uma área (em vermelho) que deverá ser replicada para preencher o espaço necessário. As áreas amarelas também serão replicadas nos sentidos indicados. Para realizar um comportamento semelhante ao método anterior, basta definir os insets com tamanho zero (ou "negativo"). Ex.

// defino os insets com o right e o bottom exagerados, para que apenas 1px seja replicado
UIImage *image = [[UIImage imageNamed:@"stretch_tile.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(48, 38, 1000, 1000)]; 
self.imageView.image = image;

UIView contentStretch

As soluções anteriores agem diretamente sobre a UIImage. Existe um outro caminho que age sobre o container (UIView):

@property(nonatomic) CGRect contentStretch; //Em UIView.h

Este método causa um comportamento ligeiramente semelhante aos anteriores, mas é utilizado na UIImageView e não na UIImage.

self.imageView2.contentStretch = CGRectMake(0.1, 0.05, 0.7, 0.8);

Repare que neste caso, o retângulo de referência não é mais definido em pixels, mas em proporções (de 0.0 a 1.0). No exemplo acima, o retângulo terá a origem em (10%, 5%) do tamanho da UIImageView e seu tamanho será de (70%, 80%) da mesma.

Conceito do UIView contentStretch

Limitações

Gradientes

Os métodos que atuam sobre a UIImage e o contentStretch manipulam a imagem de maneiras diferentes. Essa diferença é perceptível em alguns casos como no redimensionamento de uma imagem com gradiente. Veja os exemplos:

Diferenças entre a UIImage com stretch/resize e o uso do contentStretch

Somente o contentStretch de fato estica a área central, permitindo que o gradiente se distribua mais uniformemente. Se for necessário que uma imagem se expanda no mesmo sentido que o gradiente, então essa técnica é a mais indicada. Se a expansão for majoritariamente no eixo perpendicular ao gradiente, então todas terão o efeito semelhante:

Semelhanças entre a UIImage com stretch/resize e o uso do contentStretch

UIButtons

O caso mais comum de uso de imagens como as anteriores é como fundo dos UIButton. Infelizmente, o comportamento do contentStretch nos UIButton não é o esperado e não influencia a maneira como a imagem de fundo é exibida. Neste caso, precisamos usar os métodos de UIImage para resolver nosso problema:

[self.button setBackgroundImage:image forState:UIControlStateNormal];

Contudo, quando queremos que o botão realce ou esmaeça a imagem se selecionado ou desabilitado, preenchemos as propriedades adjustsImageWhenHighlighted e adjustsImageWhenDisabled. Infelizmente, ao usarmos a técnica acima, os efeitos de highlight e de desabilitado são aplicados de maneira errada.

UIButton com o highlight e disable errados

Para contornar esse problema, precisamos criar outras duas imagens, uma para cada estado e associá-las ao UIButton.

[self.button setBackgroundImage:imageDisable forState:UIControlStateDisabled];
[self.button setBackgroundImage:imageHighligh forState:UIControlStateHighlighted];

Outras técnicas

Todas as soluções acima são nativas do iOS. Existem soluções de terceiros, que ainda não testamos, que permitem, p. ex. utilizar o 9-patch de maneira semelhante ao Android. Se alguém tiver a oportunidade de usar essa ou outra solução e quiser comentar aqui, não se acanhe.

Ao navegar neste site, você consente o uso de cookies nossos e de terceiros, que coletam informações anônimas e são essenciais para melhorar sua experiência em nosso site.