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.
A segunda opção, e provavelmente a escolhida pela maioria, é criar uma imagem para cada cenário onde será utilizada, chegando aos resultados abaixo.
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:
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:
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.
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:
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:
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.
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.