Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Transformer une vue (UIView) en image (UIImage)

Compatible iOS 3. Cliquer pour en savoir plus sur les compatibilités.Compatible iOS 4. Cliquer pour en savoir plus sur les compatibilités.Par simplearetenir (Simplearetenir), le 02 juillet 2010

Dans ce tutoriel, je vous propose deux moyens de transformer une vue en image. Un moyen très simple en 4 lignes et un autres un peu plus complexe qui vous permettra plus d'interaction. Transformer une vue en image peut s'avérer utile pour envoyer le visuel d’une vue par mail par exemple.

On va utiliser quelques fonctions de Quartz.

Dans le .h

Donc dans le .h, il faut déclarer votre future méthode, par exemple :

- (UIImage *)imageRepresentationOfView:(UIView*)myView;

Dans le .m

- (UIImage *)imageRepresentationOfView:(UIView*)myView {
 
}

1. Méthode simple

Il existe un moyen très simple de faire.

Création du contexte de dessin et insertion du visuel de notre vue

Tout d’abord il faut créer le contexte de dessin dans lequel on va « dessiner » notre image. Rien de plus simple, il existe une fonction de UIKit (UIKit Function Reference) qui va le faire, il suffit de spécifier les dimensions de l’image que l’on veut obtenir en sortie. On va lui donner les dimensions de notre vue de départ :

UIGraphicsBeginImageContext(myView.bounds.size);

Ensuite on utilise la fonction renderInContext, qui permet de générer dans un contexte donné le contenu d’un layer. Donc on utilise [myView layer] pour récupérer le layer de la vue et pour récupérer le contexte courant, UIGraphicsGetCurrentContext(), ce qui nous donne :

[[myView layer] renderInContext:UIGraphicsGetCurrentContext()];
Qu'est-ce qu'un « layer » ? Une traduction de « layer » est calque. On retrouve ce terme ici, comme d'ailleurs dans la majeur partie des langages ou des logiciels de retouche d'image. Notre vue est composé de plusieurs contrôles, chacun de ces contrôles est composé de composant graphique qui permettent de le représenter à l’écran. Pour dessiner un composant, il faut afficher son « layer », le layer d’un contrôle est donc composé des layers de ses composants. Le layer d’une vue est donc la somme des layers de ses contrôles.

Création de notre UIImage

Il faut maintenant recupérer le visuel de notre contexte, pour cela on utilise UIGraphicsGetImageFromCurrentImageContext qui comme son non l’indique (en anglais) retourne une image à partir du contexte courant.

UIImage * myImage  = UIGraphicsGetImageFromCurrentImageContext();

On n'en a fini avec notre contexte, on ne va plus rien y ajouter alors on s’en débarrasse proprement :

UIGraphicsEndImageContext();

Voilà c'est fini !

Et on retourne notre image :

return myImage;

Le code complet

- (UIImage *)imageRepresentationOfView:(UIView*)myView {
	UIGraphicsBeginImageContext(myView.bounds.size);
	[[myView layer] renderInContext:UIGraphicsGetCurrentContext()];
	IImage * myImage  = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	return myImage;
}

2. Méthode un poil plus évoluée

Il existe un autre moyen en utilisant la fonction CGBitmapContextCreate, le souci avec celle-ci c’est que c’est un brin plus complexe. En effet il faut gérer un colorSpace, la représentation en mémoire, ainsi que l’alpha de notre image finale. Je vais décrire comment l’utiliser mais je vous invite a voir la doc Apple (CGBitmapContext Reference) pour plus de précisions sur chaque étape.

Création d'un CGColorSpace

On commence par déclarer une référence sur un CGColorSpace, c’est un type qui encapsule les informations d’affichage de l’environnement. Suivant la machine qui lance notre code, ces informations peuvent être différentes (un iphone 3G n’aura pas les mêmes infos que l’iPhone 4 ou encore l’iPhone 12 ☺) .

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

Ici on utilise la fonction CGColorSpaceCreateDeviceRGB, il en existe d’autres suivant la qualité d’image que vous voulez reproduire ou plus précisément l’aspect de rendu des couleurs, vous pouvez retrouver plus de précisions dans la doc Apple (CGColorSpace Reference).

Création du CGContextRef

Ensuite créons un CGContextRef, celui-ci est une représentation Quartz 2D où l’on va pouvoir « dessiner » des choses, ici nous allons y « dessiner » une image.

CGContextRef contentContext = CGBitmapContextCreate (NULL, myView.bounds.size.width,myView.bounds.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

Le premier paramètre est à NULL, on ne s’en souci pas (c’est un paramètre qui sert à la représentation en mémoire de nos dessins, en passant NULL on laisse Quartz se débrouiller).

Les deux paramètres suivant indiquent les dimensions en sortie de notre contexte, le premier étant la largeur, le second la hauteur, en pixels. Donc ici on met les dimensions de notre vue.

Les quatrième et cinquième paramètres correspondent aux infos de codages des bits utilisées pour la représentation notre future image.

Le sixième, c’est notre colorSpace (voir plus haut).

Et enfin le dernier paramètre sert a donné les infos de l’image, par exemple une éventuelle gestion d’un alpha. Aller voir la doc Apple (CGImage Reference pour plus d’explications).

Soyons propre, release du CGColorSpace

On aura plus besoin de notre colorSpace donc on le « release », notez que le colorSpace étant un type il faut utiliser une fonction de release, ce n’est pas un objet.

CGColorSpaceRelease(colorSpace);

Vérification de notre CGContextRef

On vérifie que la création du contexte a bien réussi, si ce n’est pas le cas on sort en renvoyant nil :

if (contentContext == nil)
	return nil;

Petite modification de notre contexte

Le contexte créé par défaut est inversé, c’est-à-dire a l’envers, cela est dû au système de coordonnées, l’origine (0 ;0) étant en haut à gauche. Donc si on ne fait rien l’image que l’on va générer sera à l’envers avec une impression de la voir à travers un miroir. Pour y remédier, il suffit d’appliquer « un retournement », pour cela on utilise la fonction CGContextScaleCTM qui permet d’agir sur les dimensions (l’échelle) du contexte, l’astuce c’est de mettre la transformation verticale à -1 pour procéder au « retournement »

CGContextScaleCTM(contentContext, 1, -1);

Le problème c’est que cette transformation décale le contexte vers le haut (il a été retourné) donc on le décale de toute sa hauteur.

CGContextTranslateCTM(contentContext, 0, -myView.bounds.size.height);

Dessinons notre image

Ensuite pour « dessiner » dans le contexte le contenu de notre vue, il suffit d’utiliser la fonction :

[[myView layer] renderInContext:contentContext];

Création de notre UIImage

Voilà le contexte contient maintenant le dessin de notre vue, pour pouvoir l’exploiter il faut créer une image. Plus précisément un UIImage. Pour y arriver on crée d’abord un CGImageRef :

CGImageRef contentImageRef = CGBitmapContextCreateImage(contentContext);

La dernière étape : la création de notre image :

UIImage *myImage = [UIImage imageWithCGImage:contentImageRef];

Encore un peu de ménage

Voilà on a plus besoin de notre contexte alors on s’en sépare proprement :

CGContextRelease(contentContext);

On « release » notre CGImageRef :

CGImageRelease(contentImageRef);

Voilà c'est fini !

Et on retourne notre image :

return myImage;

Le code complet

- (UIImage *)imageRepresentationOfView:(UIView*)myView {
	CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
	CGContextRef contentContext = CGBitmapContextCreate (
		NULL,
		myView.bounds.size.width,myView.bounds.size.height,
		8, 0, colorSpace, kCGImageAlphaPremultipliedLast
	);
	CGColorSpaceRelease(colorSpace);
	if (contentContext == nil)
		return nil;
	CGContextScaleCTM(contentContext, 1, -1);
	CGContextTranslateCTM(contentContext, 0, -myView.bounds.size.height);
	[[myView layer] renderInContext:contentContext];
	CGImageRef contentImageRef = CGBitmapContextCreateImage(contentContext);
	UIImage *myImage = [UIImage imageWithCGImage:contentImageRef];
	CGContextRelease(contentContext);
	CGImageRelease(contentImageRef);
	return myImage;
}



Conclusion

Voilà vous êtes maintenant capable de créer un image à partir d'une vue.

Pour aller plus loin

Il existe un autre tutoriel sur comment redimensionner une image, tutoriel rédigé par un très bon développeur, je vous invite à y jeter un œil.

En savoir plus

La documentation Apple pour toutes les subtilités…