Vous êtes sur la page 1sur 409

PROGRAMMEZ

pour

iPhone,
iPod touch,
iPad
avec iOS 4

iPuP

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

PROGRAMMEZ POUR
IPHONE, IPOD TOUCH, IPAD
AVEC IOS 4
IPUP

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous
fournir une information complte et fiable. Cependant, Pearson Education France nassume
de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux
droits de tierces personnes qui pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les
descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou
professionnelle.
Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices
ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples
ou programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.

Publi par Pearson Education France


47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00
www.pearson.fr
Collaboration ditoriale : Herv Guyader
Ralisation pao : La B.
ISBN : 978-2-7440-4168-6
Copyright 2010 Pearson Education France
Tous droits rservs

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle
L. 122-5 2 et 3 a) du code de la proprit intellectuelle ne peut tre faite sans lautorisation
expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues
larticle L.122-10 dudit code.

00_TDM_iPhone.indd 2

18/08/10 11:43

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

Table des matires

Chapitre 1
1
2
3

Configurer son environnement . . . . . . . . . . . . . . . .

Pourquoi dvelopper pour cette plate-forme ? . . . . . . . . . . . . . . . . . .


Par o commencer ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
O trouver les ressources adquates ? . . . . . . . . . . . . . . . . . . . . . .

3
6
13

Chapitre 2 Le Hello iPuP .


4
5
6
7

. . . . . . . . . . . . . . . . . . . . . . . . . . .

Introduction Xcode . . . . . . . . . . .
Coder quelques lments simples . . . .
Premiers pas dans Interface Builder . . .
Compiler sur simulateur ou sur iPhone ?

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

21
25
27
30

. . . . . .

31

.
.
.
.
.
.

.
.
.
.
.
.

33
44
48
60
70
81

. . . . . . . . . .

87

Chapitre 3 Apprhender quelques lments dinterface


8
9
10
11
12
13

Un catalogue des lments dinterface .


Ajouter un fond dcran Hello iPuP .
Ragir aux vnements . . . . . . . . .
Crer un delegate. . . . . . . . . . . .
Prsenter des listes de donnes . . . .
Utiliser une scroll view . . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

Chapitre 4 Utiliser les fonctionnalits de liPhone .


14
15
16
17
18
19
20
21

Services autour du GPS. . . . . .


Jouer de la musique en secouant .
Multitche et notifications locales.
Communiquer par Bluetooth. . .
Jouer une vido . . . . . . . . . .
Les notifications Push . . . . . . .
Utiliser des photos . . . . . . . .
LIn App Purchase (achat intgr).

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.

19

.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

89
105
115
123
140
147
156
164

Programmez pour iPhone, iPod touch, iPad avec iOS 4

III

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

Table des matires

Chapitre 5 Aller plus loin .


22
23
24
25
26
27
28
29
30

IV

. . . . . . . . . . . . . . . . . . . . . . . . . . .

Comprendre la gestion des vues . . . . . . . .


Navigation entre vues. . . . . . . . . . . . . .
Utiliser un tab bar . . . . . . . . . . . . . . . .
Utilisation dun timer pour dplacer une vue .
Core Animation . . . . . . . . . . . . . . . . .
Passer des variables un serveur (GET/POST)
Utiliser le carnet dadresses . . . . . . . . . . .
Accder votre calendrier . . . . . . . . . . .
Intgrer de la publicit : iAd. . . . . . . . . . .

.
.
.
.
.
.
.
.
.

189
201
212
219
224
241
252
268
278

. . . . . . . . . . . . . . . . . . . . .

285

.
.
.
.
.
.

.
.
.
.
.
.

287
296
310
323
340
348

37 Et du ct de liPad ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

374

Lexique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

393

Index

397

Chapitre 6 Gestion des ressources


31
32
33
34
35
36

Traduire votre application. . . . .


La mmoire . . . . . . . . . . . .
Utilisation de SQLite . . . . . . .
Parser des fichiers XML et JSON.
Sauvegarder des donnes. . . . .
Utiliser Core Data . . . . . . . .

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

187

.
.
.
.
.
.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

Introduction

Vous tes dsireux de programmer sur iPhone ? Louvrage que vous tenez entre les mains vous
suivra tout au long de votre apprentissage.
Que vous soyez simple dbutant ou dj sensibilis au dveloppement dapplications pour iPhone,
iPod Touch ou iPad, vous dcouvrirez, au travers des fiches thmatiques, des exemples concrets de
ralisation dapplications sous forme de tutoriels. Cet ouvrage vous guidera pas pas pour apprhender les diffrentes fonctionnalits ncessaires llaboration dapplications performantes,
varies et ergonomiques pour les utilisateurs du monde entier.
Les premires fiches aideront les dbutants trouver les ressources ncessaires avant de se lancer
dans laventure. Notez que nous ne couvrirons pas ici une formation spcifique en Objective-C, le
langage de programmation objet utilis dans le dveloppement dapplications iPhone, iPod Touch et
iPad. Nous avons effectivement choisi de vous guider au travers dexemples concrets, pendant
lesquels vous apprhenderez peu peu le langage, que vous pourrez approfondir travers les
ressources rendues disponibles par Apple, notamment. Il est donc primordial de raliser les fiches
dans lordre.
Ensuite, dans la seconde partie, vous raliserez votre premire application, un Hello iPuP, le point
de dpart de bien des langages. Puis, vous dcouvrirez dans une troisime partie les lments
dinterfaces mis votre disposition pour permettre votre application dexploiter toutes les possibilits graphiques de la plate-forme.
Dans un quatrime temps, nous apprendrons ensemble utiliser les fonctionnalits natives des
appareils, comme la lecture de la bibliothque musicale ou lutilisation du GPS. Arriv ce stade,
votre soif non tanche dapprofondir vos connaissances vous amnera la cinquime partie, qui
traitera daspects ncessaires mais plus complexes du dveloppement. Vous y apprendrez, entre
autres, grer les vues, crer des animations, communiquer avec un site web travers les webservices. Enfin, la dernire partie traitera des aspects non visibles de votre application, comme la
gestion des ressources ou la sauvegarde de donnes. La dernire fiche vous donnera les diffrences
entre liPad et liPhone, le systme dexploitation tant quasiment le mme !
Le SDK iOS est bien trop riche pour tre totalement couvert dans un mme tenant. Le matre mot
est donc curiosit !
En supplment cet ouvrage, vous trouverez un espace ddi lchange autour de chaque fiche
sur notre forum communautaire : http://www.ipup.fr/forum. Cet espace contiendra tous les
codes sources crs partir de chaque projet, et les mises jour ou corrections ventuelles.
De plus, vous pourrez partager avec plus dun millier de membres, tous dveloppeurs iPhone, et
poser toutes vos questions !
Note de lecture : vous trouverez beaucoup danglicismes comme label, table view, thread, push, delegate. Cest un parti pris, en ce sens o les ressources en franais sur le Web sont encore trop peu
nombreuses pour vous permettre de trouver des rponses en crivant dans votre moteur de
recherche prfr : ajouter contrleur de navigation iPhone (qui vous donnera des rsultats concernant le contrle parental...). Le ton style forum employ tout au long de cet ouvrage est volontaire,
par souci de convivialit dans un domaine o le partage et lentraide sont ncessaires.

Programmez pour iPhone, iPod touch, iPad avec iOS 4

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

Programmez pour iPhone, iPod touch, iPad avec iOS 4

VI

Certains passages pourront tre droutants si vous tes novice en programmation Mac ou iPhone,
et donc non familier avec lObjective-C. Cest pourquoi vous trouverez un lexique expliquant
certains termes utiliss dans leur contexte, ainsi quun index qui vous permettra dapprofondir
certaines notions.
Notez par ailleurs que lcriture du code suit ces conventions :

s Un code en gras de ce type : Code, signale des lignes modifier.


s Un code gristre : Code, indique les fichiers modifier.
s Un code plus clair : Code, dsigne des mthodes.
IPUP
Lquipe iPuP, ce sont quatre amis et jeunes entrepreneurs, frachement diplms dcole dingnieur, Loann Fraillon, Aurlien Hibert, Jrmy Lagrue et Marian Paul. Ces quatre associs ont mis
leur passion pour lunivers iPhone au service des autres : en commenant par la cration dun
forum dentraide entirement ddi la programmation sur iPhone, pour dboucher ensuite sur
la fondation dune entreprise.
Les auteurs, Marian Paul et Jrmy Lagrue, que vous retrouverez sur le forum sous les pseudos
respectifs ipodishima (Twitter : @ipodishima) et Bidou (Twitter : @mrbidoux), ont rdig cet
ouvrage.
Marian et Jrmy, 23 ans, sont tous deux ingnieurs diplms de lcole nationale suprieure des
Mines de Saint-tienne, cycle Ingnieur spcialis en microlectronique, informatique et nouvelles
technologies (ISMIN). Leur cursus les a amens dcouvrir la programmation informatique, et plus
particulirement lunivers iPhone, travers un projet ralis dbut 2009 dans le cadre de leurs
tudes, en partenariat avec une PME spcialise en traitement du signal et interaction vocale
(Voxler).

REMERCIEMENTS
MARIAN PAUL
Mes tout premiers remerciements vont Anas, qui a su supporter et endurer ces longues soires
me voir travailler derrire mon cran. Je tiens galement remercier Jrmy pour ses lectures
attentives, ses remarques et conseils sans lesquels cet ouvrage ne serait pas le mme. Merci
Aurlien et Loann pour avoir support ce projet et aid la relecture.
Mes derniers remerciements vont Patricia Moncorg des ditions Pearson pour son implication
et son exprience apportes tout au long de ce projet, ainsi qu Amandine.

JRMY L AGRUE
Je tiens remercier Marian pour son implication dans la ralisation de ce livre, ainsi que tous les
membres du forum ipup.fr qui, chaque jour, nous apportent notre petite dose de bonne humeur.
Merci galement lquipe des ditions Pearson pour nous avoir suivis patiemment durant le
processus de rdaction et correction.

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

C HAPITRE 1
CONFIGURER SON ENVIRONNEMENT

01_BlocN_iPhone.indd 1

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Avant de commencer dvelopper pour iPhone, iPod Touch ou iPad, il faut


comprendre les enjeux du march des applications mobiles et les rgles
respecter. Ensuite, et une fois ces notions assimiles, vous aurez besoin
dinstaller les outils ncessaires pour raliser votre projet. Le langage utilis
est lObjective-C ; nanmoins nous ne reviendrons pas dessus dans cet
ouvrage, mais vous trouverez la Fiche 2 le rappel des documents pouvant
vous aider lapprendre.

01_BlocN_iPhone.indd 2

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Pourquoi dvelopper pour cette plate-forme ?


1

Pourquoi dvelopper pour cette plate-forme ?

a y est, vous venez de franchir une nouvelle tape dans votre qute du dveloppement
dapplications pour les appareils mobiles dApple. Besoin de vous motiver encore un peu ?

QUELQUES

CHIFFRES

Rien ne vaut quelques chiffres pour se reprsenter un march ! LiPhone a t annonc pour la
premire fois le 9 janvier 2007, et commercialis aux tats-Unis partir du 29 juin de la mme
anne. Nomm iPhone Edge, il suscita un vritable engouement malgr ses quelques limitations
techniques. Vendu aux tats-Unis 200 000 exemplaires en trois semaines, le succs fut au
rendez-vous et conforta Apple quant son implication sur le march du tlphone mobile. Et depuis
2007, Apple sort chaque anne un nouvel iPhone. Le 24 juin 2010, la quatrime version de liPhone
a t prsente.
LApp Store est la plate-forme dApple qui distribue les applications. Il existe depuis le lancement de
la deuxime gnration diPhone en 2008. Les chiffres le concernant sont eux aussi loquents :

5 000 000 000 : le nombre de tlchargements dapplications depuis la cration de lApp Store.
1 000 000 000 dollars : la somme reverse aux dveloppeurs depuis les premires ventes dapplications.

100 000 000 : le nombre dappareils fonctionnant sous iOS vendus dans le monde depuis juin 2007.
2 000 000 : le nombre diPad vendus sous 59 jours aprs sa commercialisation (soit un toutes les
3 secondes).

225 000 : le nombre dapplications disponibles sur lApp Store.


20 000 : le nombre moyen de tlchargements par jour dune application en premire position
dans le top 100 gratuit en France.

15 000 : le nombre dapplications soumises chaque semaine Apple.


800 : le nombre moyen de tlchargements par jour pour entrer dans le top 10 payant en France.
95 % : le pourcentage dapplications acceptes sous une semaine aprs lenvoi aux services dApple.
80 : le nombre moyen de tlchargement dapplications par seconde.
28 % : la part de march aux tats-Unis pour liPhone.
10 : le ratio du nombre de tlchargements application gratuite/application payante.
Ces chiffres proviennent majoritairement de la keynote (prsentation) dApple lors de la Worldwide
Developer Confrence (WWDC) de juin 2010 et de retours dexpriences de dveloppeurs francophones.

Pourquoi dvelopper pour cette plate-forme ?

01_BlocN_iPhone.indd 3

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Pourquoi dvelopper pour cette plate-forme ?

UNE

FACILIT DE DISTRIBUTION

Lorsque vous aurez achet la licence (que nous verrons la Fiche 2), vous pourrez distribuer votre
application sur lApp Store. part lacquisition de la licence et le pourcentage prlev par Apple,
aucun frais supplmentaire dhbergement ou de distribution ne vous seront demands. Cependant,
avant dapparatre sur lApp Store, votre application doit tre valide par Apple et se conformer aux
rgles annonces par la socit. Ces guidelines sont disponibles dans la documentation, point que nous
aborderons la Fiche 3.
Une fois votre application disponible sur lApp Store, il ne vous restera plus qu suivre son volution
et proposer des mises jour en fonction des commentaires des utilisateurs, le reste tant gr par
Apple.

TROIS

CIBLES DUTILISATEUR

En apprenant programmer pour iPhone, iPad et iPod Touch, vous serez mme de toucher des
publics diffrents. En effet lutilisateur de liPhone est trs nomade et souhaite avoir sa porte des
applications simples et trs pratiques. De plus, certains jeux lorsquils sont trs bien penss et raliss
deviennent vite addictifs. Lutilisateur va alors facilement y jouer lors dun trajet en mtro par exemple.
Ces jeux, tels Doodle Jump ou Angry Birds, peuvent rester plusieurs mois dans le top 100 des applications vendues dans le monde.
Par contre, liPad se veut plus familial et prsentera des applications plus volues, perfectionnes en
terme de design. Lcran de liPad (9,7 pouces, 1 024 768 pixels contre 3,5 pouces pour liPhone)
permet de prsenter une application en crant des designs labors et soigns. Les applications iPad
portent gnralement le suffixe HD (Haute Dfinition), signe de la qualit de linterface.

UN

MARCH MOBILE

Il faut toujours garder lesprit que liPhone est un tlphone mobile. Ces appareils prsentent des
contraintes dont voici une liste non exhaustive :

Performances. Un tlphone mobile na pas la puissance, la mmoire, la vitesse de calcul, laffichage graphique dun ordinateur. Il faudra donc repenser votre manire de grer les ressources si
vous tes dveloppeur sur plate-forme fixe. Cette adaptation est un peu droutante au dbut,
mais les limites de lappareil simposeront si vous ny prtez pas attention.

Connexion rseau. La plupart du temps, liPhone sera connect un rseau mobile qui impose
des contraintes de qualit du signal, de dbit maximum et de quota de trafic. Si votre application
ncessite un accs Internet, il faudra donc vrifier que le rseau est disponible et, le cas chant,
avertir lutilisateur. Labsence de cet avertissement est une raison de refus de validation de la part
dApple.

Autonomie. Lautonomie de votre appareil est limite. Faites donc toujours attention lutilisation des ressources pour ainsi minimiser la consommation de la batterie. Par exemple, si vous avez
besoin de rcuprer la position GPS de lutilisateur, pensez dsactiver le GPS lorsque vous ne
vous en servez plus ! Il en est de mme pour lacclromtre ou le gyroscope.

01_BlocN_iPhone.indd 4

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Qualit dune application. Vous ne pourrez pas proposer de version bta de votre application
qui devra ncessairement tre un produit fini (en attente ventuellement de mises jour). Cette
contrainte impose donc de travailler de manire correcte et fournir une application exempte de
tout bogues. Une application qui crash ou qui utilise des librairies prives du systme sont
aujourdhui les principaux motifs de refus de validation de la part dApple.

FAIRE

LA DIFFRENCE

Tout cela est trs joli, mais pour vous dmarquer de toutes ces applications, il faudra ncessairement
avoir le plus qui vous propulsera littralement vers le succs. Certaines applications trs compltes
mais mal vendues ne seront jamais visibles et peu tlcharges. Dautres, car moins volues et
utilises trs peu de temps, seront propulses dans le top 100 sans aucun effort, car leur objectif
sera principalement de cibler un utilisateur dsireux de consommer beaucoup sans chercher la
qualit. Cest un peu le phnomne du snack !
Le nom de votre application ainsi que les mots-cls sont trs importants. Lutilisateur doit directement se faire une ide des fonctionnalits avec ces quelques informations. Les descriptions sont en
gnral trs peu lues, placez donc votre message accrocheur dans les premires lignes..
Libre vous de reprendre un concept existant, mais la rgle de base est de proposer encore mieux
que ce qui existe.
Laspect marketing de votre application est trs important et ne doit aucunement tre nglig si vous
souhaitez percevoir une rmunration.Vous apprendrez petit petit, en exprimentant, car ce qui est
droutant, cest que chaque cas est unique.
Je vous conseille par ailleurs de vous procurer Le Guide du Marketing des Applications iPhone crit par
les responsables du site francophone applicationiphone.com1. Ce petit guide vous donnera, entre
autres, des conseils pour mettre en valeur votre application sur lApp Store, les dmarches pour
contacter les sites les plus influents, pouvant vous procurer 1 000 tlchargements lannonce de
votre application, et bien dautres choses !

1. http://www.applicationiphone.com/marketing-application-iphone/.

Pourquoi dvelopper pour cette plate-forme ?

01_BlocN_iPhone.indd 5

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Par o commencer ?
2

Par o commencer ?

Il ne suffit pas davoir de la bonne volont pour dvelopper sa premire application


mobile, il faut aussi le matriel...

LE MAC

ET LAPPAREIL

Pour dvelopper pour iPhone, iPad ou iPod Touch, un Mac est ncessaire... Cest clair, net et sans
dtour ! Vous naurez pas besoin dun modle de course, par contre un grand cran est apprci pour
afficher le simulateur iPad correctement.
Les anciennes versions Mac avec un processeur PowerPC ne sont pas supports. Il faudra donc un
modle nouvelle gnration avec Mac OS10.6.2 ou ultrieur pour faire fonctionner Xcode 3.2.3 qui
est la version actuelle lheure o ces quelques lignes sont rdiges.
Les tests sur simulateur ne suffiront pas, car le systme ne fait qumuler iOS sur votre machine.
lutilisation, vous verrez que trs souvent, tout marche correctement sur le simulateur, mais ds lors
que vous le compilez sur un appareil, quelques dsagrables surprises peuvent arriver. Je vous conseille
donc trs fortement davoir ct de vous les appareils concerns par le dveloppement de votre
application. De plus, le simulateur ne permet pas de simuler lacclromtre, le gyroscope...

Astuce
Le temps de lancement dune application en mode debug1 est souvent plus long sur appareil que sur
simulateur. Nhsitez donc pas dvelopper sous simulateur et faire des vrifications rgulires sur
votre appareil si vous ntes pas patient !

LE L ANGAGE
Le mtier de dveloppeur nest pas inn et, ce titre, tout dbutant en programmation devra travailler
pour matriser le dveloppement iPhone, iPad ou iPod Touch. Cependant, rien nest insurmontable si
vous tes patient et curieux.
Le langage de programmation utilis est lObjective-C, un langage objet surcouche du langage C. Il faudra
donc imprativement matriser ce langage.Vous pourrez tout dabord suivre les tutoriels du site du Zro
http://www.siteduzero.com/ qui seront une bonne base pour dbuter. Ensuite, vous trouverez des
ressources sur Developpez.com2. Adaptez votre apprentissage en fonction de votre niveau.
Une fois ces bases acquises, vous pourrez commencer apprendre lObjective-C, qui est un langage
objet.

Info
Si vous avez des connaissances de programmation oriente objet (POO) car venant du monde web par
exemple, vous devrez imprativement matriser le langage C pour poursuivre laventure car certaines
notions, comme les pointeurs, vous manqueront..
1. Les termes souligns de cette faon sont expliqus dans le lexique en fin douvrage.
2. http://c.developpez.com/cours/?page=lang-c

01_BlocN_iPhone.indd 6

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Vous trouverez dans la documentation officielle trois documents lire obligatoirement :

Object-Oriented Programming with Objective-C vous expliquera ce quest la programmation oriente


objet.

Learning Objective-C: A Primer vous donnera les bases de lObjective-C en quelques lignes.
The Objective-C Programming Language dcrit le langage de manire prcise. IND-ISP-ENSABLE !
Info
Si vous avez des connaissances en C++ et/ou Java, vous pourrez lire les documents suivants :

De C++ Objective-C par Pierre Chatelier, vous permettra de faire la transition si vous venez du
monde C++ (http://chachatelier.fr/programmation/objective-c.php).

De Java Cocoa par Sylvain Gamel pour aller de Java lObjective-C ! (http://sylvain-gamel.
developpez.com/tutoriel/mac/cocoa/java/)

TLCHARGER XCODE
Xcode est lIDE (Integrated Development Environment) ncessaire pour dvelopper votre application.
Xcode permet entre autres, de dvelopper pour iPhone, iPad et iPod Touch. Il est disponible gratuitement sur le Web. Pour cela, crez un compte iTunes ou prenez celui que vous possdez dj (cela
simplifiera les dmarches dacquisition de licence par la suite). Vous trouverez la dernire version de
Xcode en tlchargement ladresse suivante : http://developer.apple.com/iphone/index.action.

Figure 2.1 :
Bienvenue jeune
dveloppeur !

Info
Nhsitez pas vous rendre cette adresse, qui vous permettra de grer votre compte (voir
Figure 2.2) http://developer.apple.com/membercenter/index.action.

Par o commencer ?

01_BlocN_iPhone.indd 7

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Par o commencer ?

Figure 2.2 :
Lespace membre.

Cette version gratuite, idale pour dbuter, vous permettra de dvelopper et de tester votre application sur simulateur. Si vous souhaitez la distribuer ou la tester sur un appareil, il vous faudra acqurir
la licence, ce que nous verrons par la suite.
Linstallation crera par dfaut un dossier Developer dans Macintosh HD. Xcode sera alors accessible
par le chemin Developer > Applications.

ACHETER

LA LICENCE

Un jour ou lautre, si vous poursuivez laventure, vous serez oblig dacheter la licence. Elle vous
donnera un accs complet la documentation et aux forums de dveloppeurs Apple (en anglais) o
les ingnieurs de la socit pourront vous rpondre. De plus, cest le seul moyen pour tester votre
application sur votre appareil, et la distribuer sur lApp Store.Vous aurez galement accs aux versions
bta (versions en test interne) lorsquil y en a.
La licence gratuite est un bon dbut pour sinitier au dveloppement. Cependant, je ne peux que vous
conseiller de lacqurir le plus rapidement possible, ds vous tes sr de continuer dvelopper sur
ces plates-formes. Je le rpte, vous verrez au fil de votre exprience que le simulateur est un pige,
car il ne ragit pas exactement comme la plate-forme.
Il existe deux types de licence :

le programme Standard (iPhone Program Standard) au prix de 99 $ ou de 79 par an ;


le programme Entreprise (iPhone Developer Enterprise Program) vendu 299 $ par an.
Attention
Ne confondez pas. Le programme Entreprise permet simplement de distribuer des applications en
interne dans votre entreprise, jusqu 500 employs, sans passer par lApp Store. Le programme
Standard permet de tester vos applications sur les appareils (dans la limite de 100 par an) et
propose la distribution sur lApp Store. Vous pouvez vous inscrire ce dernier titre individuel ou au
nom dune entreprise.

01_BlocN_iPhone.indd 8

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Pour adhrer au programme Standard, il faut se rendre ladresse suivante : http://developer.apple.


com/programs/start/standard/ puis suivre les indications.
Si vous souhaitez travailler pour une entreprise, le seul moyen pour que son nom dditeur apparaisse
est quelle vous enregistre en tant que dveloppeur sur sa licence.

Info
Si vous dveloppez titre personnel, vous pouvez adhrer ce programme en tant quindividu mais
vous serez le seul dveloppeur autoris utiliser ce compte, soumettre des applications...
Dans le cas dune entreprise, vous pourrez crer une quipe de dveloppeurs auxquels vous transfrerez certains droits.
Aprs votre adhsion, vous aurez accs au portail iTunes Connect qui vous permettra de grer vos
applications. Pour cela, rendez-vous cette adresse https://itunesconnect.apple.com.

Figure 2.3 :
iTunes Connect.

De plus, sur le portail dveloppeur, vous aurez notamment accs au iPhone Provisioning Portal qui
grera les certificats pour vos applications.

Par o commencer ?

01_BlocN_iPhone.indd 9

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

Par o commencer ?

Figure 2.4 : Lantre


des certificats.

CRER

UN CERTIFICAT

La premire tape est de crer un certificat qui sera transmis Apple et vous permettra dinstaller
des applications sur vos appareils. Pour cela :
1. Lancez lapplication Trousseau daccs que vous trouverez dans Applications > Utilitaires.
2. Cliquez sur le menu Trousseau daccs puis Assistant de certification > Demander un certificat
une autorit de certificat...
3. Remplissez ensuite les informations comme la Figure 2.5, nous allons enregistrer cette
demande de certificat sur le disque. Ce fichier aura pour extension .certSigningRequest.
4. Vrifiez que la fentre suivante est la mme que la Figure 2.6.
Comme je lai dit, les certificats se grent sur le portail dveloppeur.
5. Rendez-vous http://developer.apple.com/iphone/ puis cliquez sur iPhone Provisioning
Portal et Certificates.
6. Faites ensuite add Certificates et envoyez le fichier que nous venons de crer.
7. Vos dveloppeurs valident le certificat. Ils doivent cliquer sur Approve dans Team Signing
Requests.
8. Tlchargez le certificat et installez-le sur votre ordinateur (en double-cliquant dessus).

01_BlocN_iPhone.indd 10

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 2.5 : Demandez un certificat


de dvelopeur.

Figure 2.6 : Scurisez le tout.

Figure 2.7 :
Votre nouveau
certificat.

Par o commencer ?

01_BlocN_iPhone.indd 11

11

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

Par o commencer ?

Info
Le certificat est li votre machine. Si vous souhaitez dvelopper sur plusieurs machines, il faut
vous rendre dans le Trousseau daccs puis cliquer sur Cls. Ensuite, reprez la cl correspondant
votre certificat de dveloppeur puis cliquez du bouton droit et Export Votre Nom (voir Figure 2.8).
Enregistrez le fichier .p12 puis ajoutez un mot de passe.
Cette tape est trs importante et je vous conseille de sauvegarder ce fichier mme si vous ne
possdez quune machine. Vous continuerez ainsi signer des applications depuis un autre ordinateur
si le vtre est en maintenance par exemple.

Figure 2.8 :
La cl sauver.

GNRER

DES PROVISIONINGS

Bien, cela tant fait, vous avez maintenant le droit de signer des applications pour les installer sur des
appareils. Encore faut-il autoriser votre appareil pour le dveloppement et crer un provisioning de
dveloppement. Cette tche, un peu complique, a t simplifie depuis Xcode 3.2.3, alors profitez-en !
Branchez votre appareil votre ordinateur puis lancez Xcode. Rendez-vous dans Window > Organizer
o vous devriez le voir apparatre. Cliquez dessus puis sur Use for Development. Les actions suivantes
vont se raliser :

envoi de lidentifiant de lappareil sur le Provisioning Portal (une identification vous sera demande) ;
un provisioning profile incluant lappareil va tre tlcharg dans Xcode ;
les membres de lquipe pourront dvelopper sur cet appareil une fois que Xcode aura rapatri
le nouveau provisioning.
Vous pouvez nanmoins raliser ces oprations la main, depuis le portail developer si vous souhaitez
notamment nautoriser de signer votre application que sur certains appareils.

01_BlocN_iPhone.indd 12

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

O trouver les ressources adquates ?


3

O trouver les ressources adquates ?

Le Web regorge de ressources pour rpondre toutes vos questions. En voici une vue
densemble.
Avant toute chose, il est bon de rappeler certains rflexes adopter. Lorsque vous vous posez une
question, commencez par chercher dans la documentation fournie par Apple qui est trs bien ralise
et facile daccs. Elle ncessite cependant un petit temps dadaptation pour lexploiter de manire
performante. La plupart de vos interrogations y trouveront leurs rponses. Si rien ne correspond,
rendez-vous sur le Net.

Info
Beaucoup de ressources sont en anglais, vous ny couperez pas et devrez matriser cette langue. Les
ressources en franais existent, dont le forum iPuP, mais vous devrez approfondir par vous-mme,
dans la langue de Shakespeare...

LES

PREMIERS DOCUMENTS LIRE

Comme spcifi la Fiche 2, commencez par lire les guides Apple concernant lObjective-C :

Object-Oriented Programming with Objective-C.


Learning Objective-C: A Primer.
The Objective-C Programming Language.
Ensuite, vous devrez vous familiariser avec lenvironnement iPhone. De plus, votre application devra
se conformer ce document avant dtre valide et vendue sur le Store : iPhone Human Interface
Guidelines qui dcrit le modle que vous devez calquer en terme dergonomie et dinterface utilisateur.
Lisez galement iPad Human Interface Guidelines si vous souhaitez dvelopper sur cette plate-forme.

Attention
Noubliez pas que vous ne disposez que dune seule fentre. De plus, linterface graphique et les
interactions utilisateurs doivent imprativement tre penses avant dcrire la moindre ligne de
code. Votre application devra tre utilisable de manire intuitive.
Une fois ces quelques pages lues, vous pouvez vous attaquer iPhone Application Programming Guide qui
vous donnera des conseils pour la ralisation de votre application et iPhone Development Guide pour
apprendre vous servir des outils de dveloppement.
Je vous conseille galement de vous procurer la bible des dveloppeurs Cocoa : Programmation Cocoa
sous Mac OS X par Aaron Hillegass1. Certes, ce livre traite de la programmation Mac OS X, mais vous
ferez facilement le lien entre Cocoa et Cocoa Touch utilis par iOS.

1. http://www.pearson.fr/livre/?GCOI=27440100726260

O trouver les ressources adquates ?

01_BlocN_iPhone.indd 13

13

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

O trouver les ressources adquates ?

L A DOCUMENTATION
Une fois ces quelques documents lus, vous pourrez dmarrer votre projet. Vous aurez trs souvent
besoin daccder la documentation. De plus, faites cette recherche avant de venir poser votre question sur un forum surtout si cette dernire est du type : Bonjour, jaimerais savoir comment changer
la couleur du texte dans un label ? La rponse sera immdiate : Merci de chercher dans la documentation ! Regardons comment faire.
Vous accdez la documentation de plusieurs manires :

En ligne. Vous la trouverez sur votre portail dveloppeur aprs identification ou sur ce site :
http://developer.apple.com/iphone/library/navigation/index.html.

Dans Xcode. Une copie de la version en ligne est disponible directement sous Xcode. Pour cela,
se rendre dans Help > Developer Documentation ou faire le raccourci A+a+?.

Astuce
Pour accder plus rapidement la documentation, cliquez du bouton droit sur un mot-cl comme
UILabel ou shouldAutorotateToInterfaceOrientation puis choisissez Find Text in Documentation.
Si vous ne souhaitez pas lancer lAide, reprez llment sur lequel vous souhaitez vous renseigner,
appuyez sur la touche a puis double-cliquez. Vous aurez ainsi une fentre contextuelle comme
la Figure 3.1

Figure 3.1 : Laide contextuelle.

01_BlocN_iPhone.indd 14

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Si vous cherchez par exemple vous renseigner sur UILabel, vous devriez obtenir une figure analogue
la Figure 3.2.
10
1

9
2
3
4
5
6
7

Figure 3.2 : Se familiariser avec laide.


En , vous trouverez la liste de tous les documents o apparat UILabel. Dans la colonne reprsente
par le numro  sont listes toutes les mthodes, les proprits et les explications pour exploiter
un objet de la classe UILabel.
En , vous trouverez lhritage de la classe UILabel puis en  le framework importer pour lutiliser.
Llment reprsent en  est trs important car spcifie la version minimum de lOS pour pouvoir
lutiliser.
En , vous trouverez une liste de tous les exemples de code employant la classe UILabel.

Info
Ces exemples de code (samples code) sont trs importants car ils vous montrent comment utiliser
un objet. Nhsitez donc pas les tlcharger puis les compiler et vous inspirer du code source.
En , vous trouverez une description de la classe et en la liste des mthodes et proprits
dtaille. Enfin, vous pourrez accder rapidement un endroit avec la liste droulante pointe par le
numro
.
Le champ de recherche dans la documentation est reprsent par le numro .

O trouver les ressources adquates ?

01_BlocN_iPhone.indd 15

15

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

16

O trouver les ressources adquates ?

Attention
La documentation Xcode ne concerne pas uniquement liPhone. Pour viter de chercher dans la documentation Mac des lments qui nexistent pas sur liPhone, ne recherchez que dans la documentation
iPhone comme la Figure 3.3.

Figure 3.3 : Ne donner la parole


qu liPhone

Astuce
Vous ne vous rappelez plus dune proprit dun lment ou du nom dune mthode ? Lautocompltion
na pas suffit ? Appuyez sur la touche Esc de votre clavier. Par exemple, Figure 3.4 aprs avoir dclar
UILabel *monLabel puis tap mo, vous aurez un choix parmi lesquels le nom de votre objet. Figure 3.5,
aprs avoir crit monLabel., vous aurez une liste des proprits disponibles pour ce label. Enfin, aprs
avoir tap [monLabel set, vous aurez une liste des mthodes commenant par set (Figure 3.6)

Figure 3.4 : La liste des objets


ou variables.

Figure 3.5 : La liste des proprits de UILabel.

01_BlocN_iPhone.indd 16

Figure 3.6 : La liste des mthodes de UILabel.

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

LES

RESSOURCES SUR

INTERNET

Avant de vous rendre sur le Net pour poser votre question, retournez encore une fois la documentation pour vrifier que la rponse votre question ny est pas. Ensuite, vous pourrez vous rendre sur
des forums spcialiss.Vous trouverez en anglais :

Le forum developer dApple accessible depuis votre portail. Ce forum de trs bonne qualit vous
permettra dchanger avec les ingnieurs Apple qui sy rendent trs rgulirement.

Le forum iphonedevsdk.com ou stackoverflow.com sont galement deux sources de qualit et


trs actives.Vous recevrez des rponses dans lheure, voire moins.
Il existe galement des ressources en franais dont le site spcialis ipup.fr avec son forum http://
www.ipup.fr/forum. Ce site a t cr suite une constatation trs simple : les ressources en franais pour le dveloppement iPhone taient rares. Les rdacteurs de ce livre, qui sont aussi les fondateurs du site, ainsi que le millier de membres trs actifs seront toujours prts aider les dbutants.
Une rubrique spciale est mise en place pour changer autour du livre et tlcharger les codes
sources de chaque fiche. Nhsitez pas vous inscrire, la bonne humeur ncessaire pour coder sur
iPhone, iPad ou iPod Touch sera l ! De plus, vous trouverez gnralement rponse votre interrogation dans lheure.

Attention
Jinsiste. Les dveloppeurs sont en gnral toujours prts expliquer et partager leurs
connaissances. Cependant, les questions de dbutants sont souvent poses sur le forum et
reviennent rgulirement. Vrifiez toujours avec une recherche avant de poster pour viter trop de
redondance sur un mme sujet !
Vous nobtiendrez jamais une rponse une question du type : Pourriez-vous me faire a ?. Par
contre, si vous montrez dans votre post que vous avez cherch, essay des solutions mais que vous
ny arrivez pas, la rponse sera immdiate, cordiale et explique !
Petite astuce : si vous passez par Google et que vous ne trouvez pas de fonction recherche sur
le site concern, vous pouvez essayer de taper dans le champ de recherche la requte suivante :
site:ladresse du site votre interrogation. Par exemple : site:www.ipup.fr changer la couleur dune
table view.

O trouver les ressources adquates ?

01_BlocN_iPhone.indd 17

17

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

C HAPITRE 2
LE HELLO IPUP

01_BlocN_iPhone.indd 19

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

a y est, vous tes dcid vous lancer dans laventure iPhone ! Mais par o
commencer ? Dans ce chapitre, vous dcouvrirez les outils ncessaires pour
dmarrer et concevoir une premire application trs simple.
Pour dvelopper, vous avez besoin dun IDE (Environnement de Dveloppement Intgr) appel Xcode. Ce logiciel nest pas uniquement ddi au
dveloppement iPhone, vous pouvez aussi lemployer pour dvelopper des
applications en Java ou en C++. Dans notre cas, nous allons crire nos
applications avec le langage Objective-C.

01_BlocN_iPhone.indd 20

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Introduction Xcode
4

Introduction Xcode

Xcode sinstalle partir du DVD dinstallation systme reu avec votre Mac. Aprs installation, il sera
rang par dfaut dans le dossier Macintosh HD/Developer/Applications. Dans notre cas, il a d sinstaller en mme temps que le SDK (voir Fiche 2).
En lanant cet outil, vous devriez avoir une fentre ressemblant la Figure 4.1 :

Figure 4.1 : La fentre


daccueil de Xcode.

Comme cest votre premire utilisation de Xcode, la fentre Recent Projects est vide. Pour vous
familiariser avec Xcode, puis avec Interface Builder, vous allez construire votre premire application :
un Hello iPuP !
Commenons par crer un nouveau projet : ouvrez File > New project ou cliquez, dans la fentre
douverture, sur Create a new project (*+A+N). Vous avez le choix entre plusieurs modles
(templates). Ce sont en fait des projets avec une architecture toute prte. Ils sont trs utiles lorsque
vous dbutez, car ils vous aident comprendre et construire correctement une architecture de vue.
En voici le dtail :

Navigation-based Application. Ce projet contient une navigation bar (barre de navigation) dj


mise en place. Une barre de navigation (UINavigationBar gre par un UINavigationController)
est trs pratique quand vous affichez des listes de donnes (table view), linstar de celle du
carnet dadresses sur votre iPhone (UITableView).

OpenGL ES Application. Une vue OpenGL ES dj configure, avec un carr rempli dun
dgrad qui tourne sur lui-mme.

Tab Bar Application. Un tab bar ou barre donglets est une barre situe en bas de lcran vous
permettant de choisir parmi plusieurs vues par un simple clic. Lapplication iPod par exemple en
compte un. Un tab bar est gr par un UITabBarController.

Utility Application. Vous aurez trs certainement besoin de faire des transitions entre vos vues,
et cette base de projet vous propose une solution afin de raliser une animation de type flip
(retournement 3D de 180).

Introduction Xcode

01_BlocN_iPhone.indd 21

21

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

Introduction Xcode

View-Based Application. En commenant par ouvrir un projet de ce type, vous aurez une vue
qui saffichera par-dessus la fentre (UIWindow). Cette vue sera gre par un viewController
(UIViewController) charg depuis un fichier .xib (fichier utilis avec Interface Builder).

Info
Vous trouverez parfois crit fichier nib. Cest un fichier .xib, mais avec lancienne dnomination pour
NextStep Interface Builder.

Window-based Application. Le projet de base, compltement pur.


Pour notre part, nous allons crer un projet Window-based Application, que nous nommerons HelloiPuP.
Vous arrivez sur la fentre de la Figure 4.2.
1

2
3

Figure 4.2 :
Fentre daccueil
du projet
Hello iPuP.

Examinons la barre de menu (zone ) :

Overview. Permet de choisir la cible de compilation, ainsi que les modes de compilation :
debug. Ce mode utilise les points darrts et excute le code pas pas.
release. Lexcution est plus rapide que le mode debug, mais la diffrence nest pas
toujours visible.
distribution. Cre lexcutable en vue de la distribution sur lApp Store.
Action. Propose quelques actions comme Reveal in Finder (pour ouvrir lemplacement o est
rang votre fichier), galement accessibles en cliquant du bouton droit dans lditeur.

Breakpoints. Active le mode debug avec les points darrts.


Build and run. Compile et lance votre application.
Tasks. Arrte le droulement de lapplication.
Info.
Champ de recherche.

Descendez votre regard... mais quoi correspondent tous ces fichiers (zone ) ?

CoreGraphics.framework, Foundation.framework, UIKit.framework. Vous vous en doutez


peut-tre, ce sont les librairies fournies par Apple que vous allez utiliser dans votre application.

HelloiPuP.app. Cest lapplication que vous pourrez installer sur liPhone, si vous avez pay votre
licence...

01_BlocN_iPhone.indd 22

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

HelloiPuP_Prefix.pch. Cest un autre fichier include. Vous navez cependant pas besoin de linclure dans vos fichiers, car il est compil sparment.

HelloiPuPAppDelegate.h. Cest un fichier den-tte trs proche de ce que vous trouvez en C


ou C++. Il contient toutes les dclarations.

HelloiPuPAppDelegate.m. Cest ici que vous allez commencer. Ce fichier va contenir les
instructions de votre programme.

HelloiPuP-Info.plist. Ce fichier contient diverses informations propos de votre programme.


Vous nallez pas vous en servir avant de compiler votre application sur votre iPhone, iPod Touch
ou iPad.

main.m. Comme dans beaucoup de langages, ce fichier contient la fonction main. Cest l o
lexcution du programme dbute.Typiquement, ce main lance le programme.Vous ne devriez pas
avoir besoin de lditer.

MainWindow.xib. Ce fichier contient linterface graphique de votre fentre. En double-cliquant


dessus, vous lancerez Interface Builder pour dessiner votre interface.
Regardons ensuite la colonne de gauche (zone ), nomme Groups & Files.Vous y trouvez notamment :

Votre projet HelloiPuP. Dans ce projet, plusieurs dossiers :


Classes. Vos classes. Il ny a ici que votre application delegate.
Other sources. Le prefix header et le main.
Ressources. Les ressources pour votre projet. Il vous faudra glisser ici les ressources
dont vous aurez besoin dans votre application (images, vidos, fichiers xib...).
Frameworks. Les frameworks ajouter votre projet.
Products. Typiquement, votre .app qui contient lexcutable.
Targets. La cible lorsque vous compilez.
Executable. Lexcutable de lapplication aprs compilation.

Find Results. Lhistorique de vos recherches.


Bookmarks. Liste lensemble des rfrences ajoutes manuellement votre code.
SCM (Software Control Management). Sert quand vous souhaitez utiliser un outil de gestion
de version (Subversion, Perforce et SCV sont supports).

Project symbols. Liste tout ce qui est utilis dans votre projet : lments dinterface, classes,
noms de mthode, instances dobjets...

Des dossiers intelligents. Ils regroupent vos fichiers dimplmentation (.m) ou les fichiers
interface builder (.xib).

Introduction Xcode

01_BlocN_iPhone.indd 23

23

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

24

Introduction Xcode

Cliquez sur Classes puis sur HelloiPuPAppDelegate.m, qui se trouve dans le dossier slectionn. Du
code apparat dans la fentre ddition comme la Figure 4.3, et la barre de fonction sactive.
Vous y trouvez, de gauche droite, des flches pour naviguer dans lhistorique des fichiers consults
, lhistorique , la liste des mthodes , la liste des signets (Edit/Add to Bookmarks (A+D)) ,
la liste des points darrts , les classes et superclasses, ainsi que les catgories (ex. :
MaClasse(Animation)) , la liste des fichiers utilisant la classe dite , le bouton vous permettant
de naviguer aisment entre .h/.m .

5
1

7
6

Figure 4.3 : Explication


des boutons intgrs.

Maintenant que les bases sont poses, codons un peu !

01_BlocN_iPhone.indd 24

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Coder quelques lments simples


5

Coder quelques lments simples

Nous allons ajouter le label UILabel notre vue afin dafficher le texte HelloiPuP.
Pour cela, il faut modifier le fichier HelloiPuPAppDelegate.h :
#import <UIKit/UIKit.h> 

@interface HelloiPuPAppDelegate : NSObject <UIApplicationDelegate> { 


IBOutlet UILabel *helloLabel; 
UIWindow *window; 
}
@property (nonatomic, retain) IBOutlet UIWindow *window; 
@end
Attardons-nous un peu sur ce code :
La ligne  est lquivalent de #include.
La ligne  dclare la classe HelloiPuPDelegate hritant de NSObject et qui est delegate de UIApplicationDelegate. La structure tant la suivante :

@interface MaClasse : SuperClass <Delegate1, Delegate2, ...>


la ligne , le mot-cl IBOutlet lie votre objet Interface Builder.
La ligne  est la dclaration de la premire vue dans la pile de vues : votre fentre !
La ligne  facilite lcriture et clarifie le code grce un mcanisme trs pratique remplaant les
getters et setters. Les nonatomic et atomic sont des paramtres spcifiant le comportement de
votre objet en multithreading. Concrtement, utiliser atomic sous-entend que laccs cet objet sera
bloqu par le thread appelant (aucun autre ne pourra y accder si lobjet reste verrouill). Si deux
threads veulent modifier lobjet, ils le feront lun aprs lautre. Avec nonatomic, le comportement ne
sera pas garanti, mais laccs sera plus rapide.
Vous pouvez ensuite spcifier le comportement mmoire, en choisissant parmi les paramtres retain,
copy ou assign. Nous ne nous y attarderons pas ici, mais je vous invite vous y pencher le plus rapidement possible. En effet, il ny a pas de garbage collector sur liPhone ! Il faut donc grer votre
mmoire trs prcisment !

Info
Garbage collector est traduit en franais par ramasse-miettes. Cest un sous-systme qui gre la
mmoire : ds lors quune ressource alloue est inutilise, il la supprime de la mmoire.
Passons maintenant au fichier .m.
Modifiez-le ainsi :

#import HelloiPuPAppDelegate.h
@implementation HelloiPuPAppDelegate 
@synthesize window; 

Coder quelques lments simples

01_BlocN_iPhone.indd 25

25

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

Coder quelques lments simples

- (void)applicationDidFinishLaunching:(UIApplication *)application { 
[helloLabel setText:@Hello iPuP]; 
// quivalent : helloLabel.text = @Hello iPuP;
[window makeKeyAndVisible]; 
}
- (void)dealloc { 
[window release]; 
[super dealloc];
}
@end

Les lignes 
permettent dimplmenter les diffrentes mthodes de la classe.
 est obligatoire lorsque vous crivez @property dans le fichier .h.
 est une mthode automatiquement lance lorsque le chargement de lapplication est termin.
 signifie que lon met le texte Hello iPuP dans le label helloLabel.
 rend la fentre (window) visible.
 mthode dealloc : mthode appele lorsque lobjet de classe est release, cest--dire libr
de la mmoire. Par exemple, si vous aviez un objet de la classe Chien allou par une instance de la
classe Ferme avec le mcanisme habituel Chien *unChien = [[Chien alloc] init];, un [unChien
release]; appellera automatiquement la mthode dealloc de la classe Chien. Dans cette mthode,
il faut donc librer tous les objets encore en mmoire.
Ici, window est release , puis le dealloc de super (la classe dont on hrite, NSObject ici) est appel.

01_BlocN_iPhone.indd 26

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Premiers pas dans Interface Builder


6

Premiers pas dans Interface Builder

Interface Builder est un outil de dveloppement propos par Apple pour faciliter les crations dInterface
Homme-Machine (IHM). Bien entendu, vous pouvez tout faire en dur, cest--dire par la programmation.
Parfois mme, vous naurez pas le choix, mais vous vous en rendrez compte le moment venu !
Pour linstant, ouvrez MainWindow.xib dans Resources en double-cliquant dessus.
A

Figure 6.1 : Fentres


d Interface Builder.
4

gauche , vous dcouvrez la librairie, qui contient tous les lments du UIKit que vous pourrez
utiliser dans votre application.
Au centre , votre vue (ici window) telle quelle apparatra sur votre iPhone.
La fentre en dessous  contient tout ce dont vous aurez besoin pour faire marcher correctement
Interface Builder :

Files owner (propritaire du fichier). Cest un objet qui tablit la communication entre les fichiers
xib et les lments de votre application. Lavantage de la substitution, cest que les connexions et
configurations faites dans le Files owner sont appliques lobjet rel quand on le charge.

First responder. Lorsque votre objet est first responder, il sera le premier intercepter les vnements, tels que copier/coller, manipulations de texte, actions que vous avez cres, etc. Le First
responder est chang dynamiquement en fonction de la hirarchie des vues notamment. Si la vue
qui est first responder ne rpond pas un vnement, alors cet vnement traversera la hirarchie
des vues jusqu tre intercept.
Enfin, droite , cest lInspecteur, avec 4 onglets : Attributs
tity (identit) D .

, Connections

Premiers pas dans Interface Builder

01_BlocN_iPhone.indd 27

, Size (taille)

, Iden-

27

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

Premiers pas dans Interface Builder

Plaons notre label en faisant un glisser-dposer depuis la librairie.


Puis centrez-le, comme la Figure 6.2.

Attention
Une vue iPhone fait 320 480 pixels. Avec une barre de statut, elle est rduite 320 460 pixels.
Une vue iPad quant elle, fait 768 1 024 pixels.

Figure 6.2 : Positionnement


du label par un glisser-dposer.

Figure 6.3 : L Inspecteur


de la taille du label.

Figure 6.4 : L Inspecteur


des attributs du label.

Centrons le texte en choisissant Align Centers pour loption Baseline dans longlet Attributs de lInspecteur (voir Figure 6.4).
Enfin, pour connecter le label qui est sur la vue au helloLabel qui appartient votre delegate, cliquez
sur Hello iPuP App Delegate puis rendez-vous sur longlet Connections dans lInspecteur. Reprez
helloLabel, et cliquez sur le rond ct. Maintenez et glissez-le jusquau label prsent dans la vue.
Relchez, sauvegardez et quittez !

01_BlocN_iPhone.indd 28

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 6.5 : Connexion


du label.

De retour sous Xcode, faites un Build/Build and Run (A+R) et... Tadamm !

Figure 6.6 : Votre premire application !

Premiers pas dans Interface Builder

01_BlocN_iPhone.indd 29

29

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

30

Compiler sur simulateur ou sur iPhone ?


7

Compiler sur simulateur ou sur iPhone ?

Vous pouvez choisir de compiler sur simulateur ou sur device (votre iPhone, iPod Touch ou iPad).
Pour cela, il faut choisir la cible dans lOverview (voir Fiche 4). Pour compiler sur device, il faut avoir
pay la licence de dveloppement et fait les provisionings ncessaires (voir Fiche 2).
Compiler uniquement sur simulateur peut se rvler trs dangereux, pour les raisons suivantes :

Le simulateur tourne sur votre Mac, donc compar votre iPhone, ses ressources sont illimites !
Cela signifie, par exemple, des animations (en apparence) trs fluides, un temps de chargement
trs court, aucune limite dans le nombre dimages charges en mmoire, etc.

La gestion mmoire nest pas la mme, notamment en ce qui concerne le mcanisme dallocation/
dsallocation.

Vous navez pas accs lacclromtre et gyroscope, ni au compas, ni au capteur de proximit.


Attention
Au fur et mesure que vous avancez dans votre projet, il ne faut pas compiler uniquement sur
simulateur, sans tester sur iPhone. Les caractristiques ne sont absolument pas les mmes. Si, lors
de la phase de dveloppement, vous compiler sur simulateur uniquement, votre application risque
fort de ne pas avoir le comportement attendu lorsque vous la testerez sur iPhone !

01_BlocN_iPhone.indd 30

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

C HAPITRE 3
APPRHENDER QUELQUES LMENTS
DINTERFACE

01_BlocN_iPhone.indd 31

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Votre premire application iPhone en main, vous voulez aller plus loin...
En effet, vous sentez bien quun simple label affichant du texte ne suffira pas
pour faire de votre application un futur best-seller ! Ce chapitre va donc vous
ouvrir de nouveaux horizons sur les lments par dfaut fournis par Apple
dans le kit de dveloppement iPhone (UIKit).

01_BlocN_iPhone.indd 32

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Un catalogue des lments dinterface


8

Un catalogue des lments dinterface

Apple fournit par dfaut de nombreux lments dinterface que vous retrouvez dans les
applications prinstalles sur votre iPhone, iPod Touch ou iPad. Parcourons-les !
Vous tes maintenant capable de vous servir dInterface Builder pour placer vos lments dinterface.
Le principe est le mme que pour le label : se servir du mot-cl IBOutlet et relier le tout sous Interface Builder !
Parce que Interface Builder est trs pratique pour agencer ses vues quand on est dbutant, il faut
savoir quil est possible de tout construire dans le code. Nous allons donc profiter de cette numration des lments dinterface pour vous en montrer le mcanisme !

Info
Nous allons donner quelques bases dutilisation des lments du UIKit. Le minimum tant de les
afficher lcran.
Nhsitez pas vous rfrer leur documentation ainsi quau projet dexemple (sample code :
UICatalog) pour en savoir plus. Pour lutilisation de la documentation, rendez-vous la Fiche 3.
Certains lments explicits ici seront approfondis dans les fiches suivantes.

Figure 8.1 : Liste des lments du UIKit.

Astuce
Noubliez pas la touche q de votre clavier. Par exemple, aprs avoir crit unBouton., appuyez sur
q pour voir la liste des proprits de votre bouton que vous pouvez modifier. Bien entendu, tout
ceci se retrouve dans la documentation !

Info
Dans la suite, nous allons parcourir les lments dinterfaces votre disposition. Vous pouvez crer
un nouveau projet de type Window-based Application pour tester les diffrents morceaux de code
qui suivent.

Un catalogue des lments dinterface

01_BlocN_iPhone.indd 33

33

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

Un catalogue des lments dinterface

WINDOW
La particularit de liPhone ou liPad est de navoir quune fentre de disponible, contrairement
aux ordinateurs o le nombre de fentres nest pas impos. Lobjet de la classe UIWindow est donc
la racine de votre future hirarchie de vues. En effet, faute de pouvoir disposer de plusieurs fentres,
la navigation entre les diffrentes vues de votre application se fera par empilements de vues.
Linitialisation de cette fentre doit se trouver dans la mthode suivante, dans votre application delegate :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Cration de la fentre
// Noubliez pas UIWindow *window dans le .h
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
// ajouts dune ou plusieurs vue(s)
[window addSubview:uneVue]; 
// afchage lcran de la fentre et ses sous vues
[window makeKeyAndVisible];
return YES;
}

INITWITHFRAME
coordonnes standard

 initWithFrame vous permet dinitialiser une vue en lui


spcifiant sa taille. Frame tant le rectangle dans lequel
elle va sinscrire.

(0,0)
x

Info
Pour crer votre propre rectangle : CGRectMake(xOrigine,
yOrigine, largeur, hauteur). Attention au repre
de liPhone (Figure 8.2). Rappelez-vous que lcran de
liPhone fait 320 480 pixels...
 La mthode addSubview: permet dajouter la vue
passe en paramtre la pile de vues.
 Cette ligne est indispensable pour afficher la fentre
ainsi que toutes ses sous-vues. makeKeyAndVisible est
propre UIWindow seulement.

Figure 8.2 : Systme de


coordonnes de l iPhone
pour le UIKit.

01_BlocN_iPhone.indd 34

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Info
Gnralement, on ne cre jamais une fentre par le code. Dailleurs, vous aurez remarqu quen crant
le projet de type Window-based Application, la fentre est dj instancie par le fichier window.xib
(avec le mot-cl IBOutlet).

VIEW
Une fois que votre fentre saffiche, vous pouvez lui ajouter des vues comme la Figure 8.3 (visualisez-le
comme un empilement). Tous les lments du UIKit sont des vues, vous pouvez donc crer votre
propre vue (par exemple votre propre liste droulante) en partant de la classe UIView.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// initialisation de la vue pour quelle remplisse le quart haut/gauche de lcran
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 240.0)];
// on change la couleur du fond de la vue pour quil soit bleu
[myView setBackgroundColor:[UIColor blueColor]];
// on ajoute la vue la window
[window addSubview:myView];
[myView release];
[window makeKeyAndVisible];
return YES;
}

IMAGE

VIEW

Cette vue vous permet dafficher une image lcran.


- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)
launchOptions {
// initialisation de la vue
UIImageView *myImageView = [[UIImageView alloc]
initWithFrame:CGRectMake(20.0,
30.0, 100, 100.0)];
// on cre une image partir de celle mise en
ressource dans le projet
UIImage *logoImage = [UIImage
imageNamed:@logo_ipup_losange.png]; 
myImageView.image = logoImage; 
// quivalent [myImageView setImage:logoImage];
// on ajoute la vue la window
[window addSubview:myImageView];
[myImageView release];
[window makeKeyAndVisible];
return YES;
}

Figure 8.3 : Votre premire vue !

Un catalogue des lments dinterface

01_BlocN_iPhone.indd 35

35

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

Un catalogue des lments dinterface

 Pour afficher la vue sur lcran, il faut dabord crer un objet UIImage partir dun fichier, ici
logo_ipup_losange.png.
 cet endroit, on spcifie limage afficher dans limage view.

Figure 8.4 : Le logo iPuP


sur le simulateur...

Info
Vous pouvez changer le comportement de limage pour quelle sadapte ou non votre rectangle dfini
avec frame (voir Figure 8.5).

myImageView.contentMode = UIViewContentModeScaleAspectFill;
Vous pouvez galement recourir une image view pour raliser de petites animations : dans la
documentation Apple : Class UIImageView > Tasks > Animating images
UIViewContentModeScaleAspectFill
Distorsion

UIViewContentModeScaleAspectFit
Pas de
distorsion

Figure 8.5 : Quelques modes


de mise en forme de l image.

UIViewContentModeScaleAspectFill
Pas de
distorsion

01_BlocN_iPhone.indd 36

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

WEB

VIEW

Une Web view vous permet de charger du contenu web, depuis Internet ou localement.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// on rcupre la taille de lcran
CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
UIWebView *myWebView = [[UIWebView alloc] initWithFrame:webFrame];
myWebView.backgroundColor = [UIColor whiteColor];
myWebView.scalesPageToFit = YES; 
[window addSubview: myWebView];
[myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL
URLWithString:@http://www.ipup.fr/]]];

[myWebView release];
[window makeKeyAndVisible];
return YES;
}
 Lorsque scalesPageToFit est YES, la page charge est directement zoome pour tre affiche
entirement et le zoom par lutilisateur est autoris.

Figure 8.6 : La page iPuP


sur votre iPhone.

Un catalogue des lments dinterface

01_BlocN_iPhone.indd 37

37

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

38

Un catalogue des lments dinterface

MAP VIEW
Cette vue est trs pratique ds lors que vous souhaitez afficher des positions GPS sur lcran.

Info
Noubliez pas dinclure le framework MapKit et faire limport suivant (voir Fiche 14) :

#import <MapKit/MKMapView.h>
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CGRect mapFrame = [[UIScreen mainScreen] applicationFrame];
MKMapView *mapView = [[MKMapView alloc] initWithFrame:mapFrame];
mapView.showsUserLocation = YES; 
[window addSubview:mapView];
[mapView release];
[window makeKeyAndVisible];
return YES;
}
 showUsersLocation YES permet de situer directement lutilisateur quelque part dans le monde
et lui montrer sa position sur la carte. Non, je ne suis pas aux tats-Unis...

Figure 8.7 : Affichez la position


de lutilisateur sur votre cran.

01_BlocN_iPhone.indd 38

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

TEXT

VIEW

Cette vue affiche du texte sans limite de taille. Si le texte dpasse la taille de lcran, des ascenseurs
sur le ct indiqueront votre position dans la vue.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CGRect textFrame = [[UIScreen mainScreen] applicationFrame];
UITextView *textView = [[UITextView alloc] initWithFrame:textFrame];
textView.text = @Vous pouvez crire plusieurs lignes de texte. Pour un
retour la ligne, utilisez simplement le caractre \\\n\. Par exemple :
\\\n\\n\ \n\n vous donne 2 retours la ligne;
textView.font = [UIFont fontWithName:@Helvetica size:17.0];
[window addSubview:textView];
[textView release];
[window makeKeyAndVisible];
return YES;
}

Info
Noubliez pas que le caractre \ permet dchapper les caractres spciaux.
Par dfaut, une textView est ditable. Vous pouvez lempcher en mettant editable NO.

Figure 8.8 : Affichez des lignes de texte.

BOUTON
Est-il ncessaire dexpliciter son utilit ? Voici comment est cr un bouton trs facilement :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
button.frame = CGRectMake(20.0, 30.0, 120.0, 30.0);
[button setTitle:@Touche moi ! forState:UIControlStateNormal];
[window addSubview:button];
[window makeKeyAndVisible];
return YES;
}

Un catalogue des lments dinterface

01_BlocN_iPhone.indd 39

39

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

40

Un catalogue des lments dinterface

 Vous remarquerez que cest une mthode de classe. En fait, au lieu dallouer et initialiser un bouton,
puis appeler [unBouton uneMethode]; vous utilisez une mthode dite de classe de UIButton.
Ainsi, lobjet UIButton retourn est en autorelease.

Info
Exemple dune mthode de classe : (vous remarquerez le +)

+(UIButton*) buttonWithType:(UIButtonType)buttonType;
 Un bouton peut avoir plusieurs tats : UIControlStateNormal,
UIControlStateSelected, UIControlStateHighlighted,... Pour chaque
tat, on peut dfinir une image, une couleur, un titre...

Figure 8.9 : Un bouton qui


attend quon le touche.

TEXTFIELD
Cet objet vous servira pour entrer du texte.Vous pouvez spcifier le type du clavier (entier, chiffres
seulement...), si lautocorrection est active ou non (autocorrectionType), si lentre du texte est dite
scurise (les points) : secureTextEntry.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UITextField *textField = [[UITextField alloc]
initWithFrame:CGRectMake(20.0, 40.0, 160.0, 30.0)];
textField.textColor = [UIColor blackColor];
// on ajoute un contour rond autour du textField
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.font = [UIFont systemFontOfSize:17.0];
textField.placeholder = @<entrez du texte>;
textField.backgroundColor = [UIColor whiteColor];
// on utilise le clavier par dfaut, qui est entier
textField.keyboardType = UIKeyboardTypeDefault;
// on spcie le type du bouton en bas droite du clavier
textField.returnKeyType = UIReturnKeyDone;
// mettre un bouton x la droite pour effacer
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
[window addSubview:textField];
[textField release];
[window makeKeyAndVisible];
return YES;
}

Info
Pour rtracter le clavier, rendez-vous la Fiche 10 !

01_BlocN_iPhone.indd 40

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 8.10 : Un champ


de texte et son clavier.

SLIDER
Le slider est un lment qui autorise lutilisateur choisir une valeur entre un maximum et un
minimum en le faisant glisser du doigt.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UISlider *slider = [[UISlider alloc]
initWithFrame:CGRectMake(20.0, 40.0, 160.0, 30.0)];
slider.minimumValue = 0.0;
slider.maximumValue = 100.0;
[window addSubview:slider];
[slider release];
[window makeKeyAndVisible];
return YES;
}

Info
Pour rcuprer la valeur du slider : slider.value.
Figure 8.11 : Un simple slider.

Un catalogue des lments dinterface

01_BlocN_iPhone.indd 41

41

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

42

Un catalogue des lments dinterface

ACTIVITY

INDICATOR VIEW

Cette vue avertit lutilisateur quun traitement est en cours, par exemple quand lapplication charge
des donnes. Il est ainsi rassur et ne pense pas quil sagit dun bogue.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// on met la window en gris pour voir lactivity indicator view
window.backgroundColor = [UIColor grayColor];
UIActivityIndicatorView *actView = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
actView.center = CGPointMake(40.0, 50.0); 
// on peut faire un actView.center = window.center;
[actView startAnimating]; 
[actView setHidesWhenStopped:YES]; 
[window addSubview:actView];
[actView release];
[window makeKeyAndVisible];
return YES;
}
Figure 8.12 : Une
 On dfinit ainsi le centre de la vue, qui est un CGPoint.

roue tournante.

 On lance lanimation de lActivity indicator view.


 Lorsque lon stoppera lanimation, lActivity indicator view sera immdiatement cache (setHidden:YES).

Info
Vous aurez trs certainement besoin de dtacher un thread pour charger des donnes, et animer
lActivity indicator view sur le thread principal. La notion de thread est assez complexe. Comprenez
quavec ce mcanisme, vous serez capable de charger des donnes en tche de fond.
Rappelez-vous cependant quune modification dlments du UIKit (les lments graphiques donc)
doit se faire sur le thread principal.
La gestion des threads nest pas vidente, vous devez avoir une bonne matrise de la gestion des
ressources. Nhsitez pas lire les guides proposs par Apple sy rfrant.
Voici un exemple dutilisation des threads, qui vous sera certainement trs pratique :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
window.backgroundColor = [UIColor grayColor];
// dclarer actView dans le .h
actView = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
actView.center = CGPointMake(40.0, 50.0);
[self performSelectorInBackground:@selector(backgroundMethod) withObject:nil]; 
[actView startAnimating];
[actView setHidesWhenStopped:YES];

01_BlocN_iPhone.indd 42

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[window addSubview:actView];
[window makeKeyAndVisible];
return YES;
}
- (void) backgroundMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
// chargement des donnes ici
// lorsque les donnes sont charges
[self performSelectorOnMainThread:@selector(backgroundMethodDone)
withObject:nil waitUntilDone:NO]; 
[pool release];
}
- (void) backgroundMethodDone {
[actView stopAnimating]; 
// et ici continue le droulement de votre programme
}

Attention
Si vous recopiez ce code, rien ne saffichera car aucune donne nest charge sur le thread dtach.
Si vous souhaitez observer le comportement, crivez :

- (void) backgroundMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// chargement des donnes ici
// lorsque les donnes sont charges
sleep(5);
[self performSelectorOnMainThread:@selector(backgroundMethodDone)
withObject:nil waitUntilDone:NO];
[pool release];
}
 On dtache ici un nouveau thread, en tche de fond, en appelant la mthode backgroundMethod.
 Puisque lon est dans un autre thread que le principal, il faut crer un bassin dautorelease. La rgle
est la suivante : un bassin dautorelease par thread.
 Aprs avoir fini de charger les donnes, il faut revenir sur le thread principal pour continuer le
droulement de lapplication.
 Rappelez-vous, les lments du UIKit ne peuvent tre modifis que sur le thread principal.

Info
Vous pouvez galement recourir une progress view qui est une barre de chargement classique. Le
principe est le mme avec la gestion des threads. Cependant, il faut modifier manuellement la valeur
de la Progress view pour la voir bouger lcran.

Un catalogue des lments dinterface

01_BlocN_iPhone.indd 43

43

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

44

Ajouter un fond dcran Hello iPuP


9

Ajouter un fond dcran Hello iPuP

Maintenant que nous avons parcouru la plupart des lments dinterface, que diriez-vous
de passer la pratique et ajouter un fond dcran notre application iPhone HelloiPuP ?
Tout dabord, il vous faut trouver une image, de prfrence avec ces dimensions : 320 460 pixels.
Ensuite, glissez-la dans Xcode directement, dans le dossier Ressources, et cochez la case Copy items
into destination groups folder.

Figure 9.1 : Cochez pour


copier l image dans le projet.

Info
La fentre de la Figure 9.1 doit obligatoirement safficher. Si ce nest pas le cas, cest que vous avez fait
une erreur. Ne copiez pas limage dans le dossier ressources du Finder, mais bien directement dans Xcode.
Pour afficher limage en fond dans votre application, il faut passer par une UIImageView. Pour vous
guider, nous allons examiner les deux mthodes : par le code, ou en utilisant Interface Builder. Faites
une copie de votre projet, afin davoir un projet par mthode.

A JOUTER LIMAGE

PAR LE CODE

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[helloLabel setText:@Hello iPuP];
// quivalent : helloLabel.text = @Hello iPuP;
CGRect imageFrame = [[UIScreen mainScreen] bounds];
UIImageView *backgroundImageView = [[UIImageView alloc]
initWithFrame:imageFrame]; 

[backgroundImageView setImage:[UIImage imageNamed:@backgroundImage.png]];


[window addSubview:backgroundImageView]; 
[backgroundImageView release]; 
[window makeKeyAndVisible];
return YES;
}

01_BlocN_iPhone.indd 44

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

 On alloue limage view qui fera la taille de lcran. Son retain count vaut 1.
 En ajoutant limage view la fentre (en la mettant sur la pile des vues), vous la placez implicitement
dans un tableau (NSArray) de vues (accessible avec [window subviews];). Ainsi, cest ce tableau qui va
grer la mmoire de lobjet plac (augmentation du retain count de lobjet plac dans le tableau de 1).
Ici, la fin de linstruction, le retain count de backgroundImageView vaut donc 2.
 Puisque lon souhaite toujours ramener les retain count des objets 1, il faut release backgroundImageView (dcrmenter son retain count de 1). Retain count de backgroundImageView vaut maintenant 1.

Info
Lorsque le retain count dun objet vaut 0, lemplacement mmoire allou pour cet objet est libr.
Lancez ainsi lapplication. Vous remarquerez que lon naperoit plus le label. Ce qui est normal, car
rappelez-vous, les vues sont empiles.
Lorsque lapplication sexcute, le fichier window.xib va tre dcod (cest un fichier XML). Les
lments dinterface placs laide dInterface Builder vont tre positionns. Ainsi, le label est plac
par-dessus la vue. Ensuite, la mthode applicationDidFinishLaunching de lapplication delegate va
tre appele. Dans cette mthode, nous allouons une UIImageView que nous plaons sur la pile des
vues, au-dessus ! Notre image cache donc notre label... Pour y remdier, plusieurs solutions :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[helloLabel setText:@Hello iPuP];
// quivalent : helloLabel.text = @Hello iPuP;
CGRect imageFrame = [[UIScreen mainScreen] bounds];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:imageFrame];
[backgroundImageView setImage:[UIImage imageNamed:@backgroundImage.png]];
// Solution 1 : 
[window addSubview:backgroundImageView];
[window sendSubviewToBack:backgroundImageView];
// Solution 2 : 
//[window insertSubview:backgroundImageView atIndex:0];
[backgroundImageView release];
[window makeKeyAndVisible];
return YES;
}
 La premire solution consiste ajouter limage view la pile, puis lenvoyer tout en dessous de
la pile.

Ajouter un fond dcran Hello iPuP

01_BlocN_iPhone.indd 45

45

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

46

Ajouter un fond dcran Hello iPuP

 La deuxime solution vous permet de choisir lindex o ajouter la vue (la position dans la pile de
vues, 0 tant le plus bas).

En relanant votre application, votre label apparat enfin ! Mais il nest pas trs visible en noir... Qu
cela ne tienne, changeons la couleur du texte en blanc !

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[helloLabel setText:@Hello iPuP];
// quivalent : helloLabel.text = @Hello iPuP;
[helloLabel setTextColor:[UIColor whiteColor]]; 
CGRect imageFrame = [[UIScreen mainScreen] bounds];
UIImageView *backgroundImageView = [[UIImageView
alloc] initWithFrame:imageFrame];
[backgroundImageView setImage:[UIImage
imageNamed:@backgroundImage.png]];
[window insertSubview:backgroundImageView atIndex:0];
[backgroundImageView release];
[window makeKeyAndVisible];
return YES;
}
 Vous pouvez trs facilement choisir parmi des couleurs prdfinies, comme whiteColor, blackColor, blueColor, grayColor,
yellowColor...

Figure 9.2 : Un label


sur un fond d cran.

Vous pouvez galement mettre des couleurs plus prcisment :

[helloLabel setTextColor:[UIColor colorWithRed:12.0/255.0 green:100.0/255.0


blue:200.0/255.0 alpha:1.0]];

A JOUTER LIMAGE

AVEC

INTERFACE BUILDER

Pour ajouter limage de fond avec Interface Builder, faire comme


suit :
1. Repartir du projet que vous aviez lorsque vous avez
commenc cette fiche.
2. Ouvrir MainWindow.xib et glisser-dposer une UIImageView sur la window. Dans longlet Frame de limage view
ainsi dpose, remplir comme la Figure 9.3.
Figure 9.3 : Modification de
la frame de l image view.

01_BlocN_iPhone.indd 46

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

3. Cliquer dans lInspecteur sur longlet Attributes et choisir limage (voir Figure 9.4).

Figure 9.4 : Mettez l image


dans l image view.

Il faut maintenant repositionner correctement limage view dans la pile des vues. Pour cela, ranger par
liste les lments de MainWindow.xib (voir  de la Figure 9.5). Dplacer ensuite limage view depuis
cette fentre en la glissant-dposant au-dessus du label, toujours dans la mme fentre (voir  de la
Figure 9.5).
1

Figure 9.5 : Placez l image


view en fond d cran.

Une alternative est de passer par le menu Layout > Send Backward ou Layout > Send To Back.
Enfin, pour changer la couleur du texte du label, cliquez dessus. Ensuite, dans lInspecteur > onglet
Attributes mettre la couleur du texte en blanc (voir  de la Figure 9.6).

Figure 9.6 : Changez la couleur


de la police.

Ajouter un fond dcran Hello iPuP

01_BlocN_iPhone.indd 47

47

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

48

Ragir aux vnements


10 Ragir aux vnements

Vous savez placer diffrents lments dinterface.Toutefois, si vous placez un bouton, il ne


fait pas grand-chose...
Perons le mystre des boutons. Ici encore, deux solutions : passer par le code, ou utiliser Interface
Builder. Nous allons voir les deux mthodes possibles.

UTILISER

LE CODE

Recrons un nouveau projet Window Based Application. Nommez-le EventHandling.


Faites ensuite Files > New File. Choisissez iPhone OS > Cocoa Touch Class > UIViewController
Subclass et laissez coch With XIB for user interface. Nommez cette classe RootViewController.
Rangez vos fichiers comme la Figure 10.1. Pour ajouter un dossier, cliquez du bouton droit sur
Classes puis Add > New Group.

Figure 10.1 : Rangement des fichiers.

Dans le code de votre application delegate, modifiez le fichier ainsi :

EventHandlingAppDelegate.h
#import <UIKit/UIKit.h>
#import RootViewController.h
@interface EventHandlingAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *rootViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) RootViewController *rootViewController;
@end

01_BlocN_iPhone.indd 48

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

EventHandlingAppDelegate.m
#import EventHandlingAppDelegate.h
@implementation EventHandlingAppDelegate
@synthesize window;
@synthesize rootViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
RootViewController *viewController = [[RootViewController alloc]
initWithNibName:@RootViewController bundle:nil]; 
self.rootViewController = viewController;
[viewController release];
rootViewController.view.frame = [[UIScreen mainScreen] bounds];
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[rootViewController release];
[window release];
[super dealloc];
}
@end
 On initialise un objet de la classe RootViewController en lui spcifiant son nom de nib (fichier .
xib). Mettre nil le bundle sous-entend quil cherchera le .xib dans le main bundle. Je vous invite
regarder la documentation afin de comprendre larchitecture du dossier de votre application.
 Grce au @property (setter/getter), lobjet rootViewController dclar dans le .h fait rfrence
lobjet viewController allou en  avec un mcanisme implicite de retain count + 1, do le release
de viewController en .
En  on ajoute la vue du rootViewController la fentre.

Nous allons ajouter un bouton, comme la Fiche 8, dans la classe RootViewController, dans le fichier
dimplmentation :

- (void)viewDidLoad {
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
aButton.frame = CGRectMake(20.0, 30.0, 120.0, 30.0);
[aButton setTitle:@Touche moi ! forState:UIControlStateNormal];
[aButton setTitle:@Encore ? forState:UIControlStateSelected];
[aButton addTarget:self action:@selector(buttonHandleMethod:)
forControlEvents:UIControlEventTouchUpInside]; 

10

Ragir aux vnements

01_BlocN_iPhone.indd 49

49

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

50

Ragir aux vnements

[self.view addSubview:aButton];
[super viewDidLoad];
}
// Mthode pour grer les vnements du bouton
- (void) buttonHandleMethod:(id)sender { 
}
 Cette ligne permet de choisir la mthode appele lorsquun certain type dvnement sur le
bouton apparat. Ici, nous avons choisi Touch up inside qui interceptera lvnement suivant : un aprs
clic dans le bouton (lorsque le doigt sen va du bouton).
 La mthode appele lorsquon cliquera sur le bouton. Remarquez le premier paramtre : (id)
sender : ce sera le bouton lorigine de lvnement.

Implmentons cette mthode :

// Mthode pour grer les vnements du bouton


- (void) buttonHandleMethod:(id)sender {
NSLog(@bouton cliqu !); 
UIButton *theButton = (UIButton*)sender; 
[theButton setSelected:![theButton isSelected]]; 
}
 Le NSLog vous permet dafficher du texte dans la console de debug, trs pratique !
 On fait une rfrence sur le sender, que lon caste en UIButton.
 Au premier abord, cette ligne parat complique. Il nen est rien : on met le boolen selected du
bouton la valeur diffrente de selected (YES devient NO ; NO devient YES).

Compilez et lancez lapplication, cliquez sur le bouton, son titre va changer !

RECOMMENCER

EN UTILISANT

INTERFACE BUILDER

Recommencez un nouveau projet et allez jusqu ltape de cration du RootViewController. Ensuite,


crivez ce code dans votre application delegate :

#import <UIKit/UIKit.h>
#import RootViewController.h
@interface EventHandlingAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *rootViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet RootViewController *rootViewController; 
@end
 Remarquez le mot-cl IBOutlet.

01_BlocN_iPhone.indd 50

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Dans le .m :

#import EventHandlingAppDelegate.h
@implementation EventHandlingAppDelegate
@synthesize window;
@synthesize rootViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[rootViewController release];
[window release];
[super dealloc];
}
@end
Ouvrez MainWindow.xib et ajoutez un objet viewController comme la Figure 10.2

Figure 10.2 : Ajoutez


un ViewController.

Cliquez maintenant sur ce view controller puis dans longlet Identity de lInspecteur. Dans Class,
renseignez RootViewController comme la Figure 10.3.

Figure 10.3 : Renseignez


le RootViewController.

10

Ragir aux vnements

01_BlocN_iPhone.indd 51

51

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

52

Ragir aux vnements

Ensuite, cliquez sur Event Handling App Delegate et allez dans longlet Connections puis relier le
rootViewController au rootViewController (voir Figure 10.4)

Figure 10.4 : Reliez le


rootViewController.

Sauvez puis allez dans RootViewController.h :

#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
}
- (IBAction) buttonHandleMethod:(id)sender; 
@end
 Le mot-cl IBAction permet de relier une mthode qui rpondra un vnement Interface
Builder.

Ouvrez RootViewController.xib et ajoutez un bouton. Cliquez dessus, puis dans lInspecteur onglet
Attributes, remplissez comme aux Figures 10.5 et 10.6.

Figure 10.5 : Renseignez le titre


pour la configuration par dfaut.

Figure 10.6 : Renseignez le titre


pour la configuration slectionn.

Enfin, cliquez sur Files owner et allez dans longlet Connections de lInspecteur. Reliez ButtonHandleMethod: dans Received Actions au bouton et choisissez Touch up inside.Vous devriez obtenir quelque
chose de comparable la Figure 10.7

Figure 10.7 : Connexions


du RootViewController.

01_BlocN_iPhone.indd 52

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Sauvez et quittez, puis dans RootViewController.m, implmentez comme suit :

- (void)viewDidLoad {
[super viewDidLoad];
}
// Mthode pour grer les vnements du bouton
- (IBAction) buttonHandleMethod:(id)sender {
NSLog(@bouton cliqu !);
UIButton *theButton = (UIButton*)sender;
[theButton setSelected:![theButton isSelected]];
}
Compilez et lancez... Et voil ! Vous connaissez les deux mthodes. Pour la suite, nous allons prfrer
la mthode de positionnement par le code. Repartez donc du premier projet cr pour cette fiche.

A JOUTER

UN INTERRUPTEUR

Ajoutons maintenant un switch (interrupteur) sur la vue de notre rootViewController :

RootViewController.h
#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
UIButton *aButton;
UISwitch *aSwitch;
}
@end
RootViewController.h
- (void)viewDidLoad {
aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
aButton.frame = CGRectMake(20.0, 30.0, 120.0, 30.0);
[aButton setTitle:@Touche moi ! forState:UIControlStateNormal];
[aButton setTitle:@Encore ? forState:UIControlStateSelected];
[aButton addTarget:self action:@selector(buttonHandleMethod:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:aButton];
aSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(20.0, 80.0, 94.0, 27.0)];
[aSwitch addTarget:self action:@selector(switchHandleMethod:)
forControlEvents:UIControlEventValueChanged]; 
[self.view addSubview:aSwitch];
[super viewDidLoad];
}
// Mthode pour grer les vnements du bouton
- (void) buttonHandleMethod:(id)sender {
NSLog(@bouton cliqu !);
UIButton *theButton = (UIButton*)sender;

10

Ragir aux vnements

01_BlocN_iPhone.indd 53

53

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

54

Ragir aux vnements

[theButton setSelected:![theButton isSelected]];


}
// Mthode pour grer les vnements du switch
- (void) switchHandleMethod:(id)sender { 
UISwitch *theSwitch = (UISwitch*)sender;
[aButton setSelected:theSwitch.on];
}
- (void)dealloc {
[aButton release];
[aSwitch release];
[super dealloc];
}
Vous remarquez que nous avons dclar dans le .h notre bouton ainsi que le switch, afin daccder au
bouton dans switchHandleMethod:. En , on spcifie que la mthode switchHandleMethod: va tre
la mthode appele lorsque le switch changera dtat. En , on implmente la mthode qui sera
appele chaque fois que le switch changera dtat. Jai choisi ici de changer ltat du bouton. La valeur
du switch (boolen) est rcupre avec theSwitch.on.
Pour continuer, nous allons jouer avec un slider. Ajoutez donc un slider et un label votre vue. Le label
servira afficher la valeur du slider.
Dans la mthode viewDidLoad, ajoutez ces quelques lignes :

aSlider = [[UISlider alloc] initWithFrame:CGRectMake(20.0, 130.0, 150.0, 20.0)];


aSlider.minimumValue = 0.0;
aSlider.maximumValue = 100.0;
[aSlider addTarget:self action:@selector(sliderHandleMethod:)
forControlEvents:UIControlEventValueChanged];
[self.view addSubview:aSlider];
aLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 170.0, 150.0, 20.0)];
[self.view addSubview:aLabel];

Info
Noubliez pas les release dans le dealloc et la dclaration dans le .h du slider et du label :

UILabel *aLabel;
UISlider *aSlider;
Le principe est le mme que pour le switch. On observe les changements de valeurs.
Dans la mthode sliderHandleMethod :

- (void) sliderHandleMethod:(id)sender {
UISlider *theSlider = (UISlider*)sender;
// Notez que lon pourrait utiliser directement aSlider
[aLabel setText:[NSString stringWithFormat:@%.2f, theSlider.value]];
}

01_BlocN_iPhone.indd 54

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Compilez et lancez, dplacer votre doigt sur le curseur, le label affiche des valeurs diffrentes !

Info
Vous aurez sans doute remarqu la syntaxe un peu particulire : %.2f. En fait, cela permet de
choisir le nombre de chiffres aprs la virgule afficher. Ici cest donc 2 !

A JOUTER

UN CHAMP DE TEXTE

Finissons par ajouter un champ de texte (UITextField) notre vue. Ajoutez dans le .h :

UITextField *aTextField;
Modifiez la mthode viewDidLoad en y ajoutant :
aTextField = [[UITextField alloc] initWithFrame:CGRectMake(20.0, 190.0, 150.0, 30.0)];
[aTextField setBorderStyle:UITextBorderStyleRoundedRect];
[aTextField setPlaceholder:@Entre un chiffre];
[aTextField setDelegate:self]; 
[aTextField setKeyboardType:UIKeyboardTypeDefault];
[aTextField setClearsOnBeginEditing:YES];
[self.view addSubview:aTextField];
 Voici quelque chose de nouveau ! Le delegate ! Pas de panique, nous allons en parler longuement
la Fiche 11.
 Je vous demande de mettre un clavier de type par dfaut, alors que nous avons besoin de rentrer
des chiffres, car je souhaite vous montrer un mcanisme particulier utile pour la suite !
 Nous choisissons ici le comportement de votre champ de texte pour que le texte entr soit effac
ds que vous cliquez dedans.

Comme nous avons spcifi que notre classe RootViewController tait delegate de UITextField, il
faut crire ceci dans le .h :

@interface RootViewController : UIViewController <UITextFieldDelegate> {


Ensuite, il faut se tourner vers la documentation pour regarder les mthodes que lon peut ou doit
implmenter lorsque notre classe est delegate de UITextField :
Managing Editing

textFieldShouldBeginEditing: optional method


textFieldDidBeginEditing: optional method
textFieldShouldEndEditing: optional method
textFieldDidEndEditing: optional method

Editing the Text Fields Text

textField:shouldChangeCharactersInRange:replacementString: optional method


textFieldShouldClear: optional method
textFieldShouldReturn: optional method
Ici, toutes les mthodes sont optionnelles, ce qui signifie que si votre classe RootViewController
nimplmente aucune de ces mthodes, le comportement de votre application ne sera pas modifi.

10

Ragir aux vnements

01_BlocN_iPhone.indd 55

55

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

56

Ragir aux vnements

Attention
Si votre classe nimplmente pas une mthode requise, dite @required, votre application plantera
lors de son appel avec une erreur du type dans la console :

*** Terminating app due to uncaught exception NSInvalidArgumentException, reason: ***


- [VotreClasse nomDeLaMthode]: unrecognized selector send to instance adresse mmoire

Info
Lorsquune mthode est dite optionnelle, il ne faut pas que lapplication plante quand vous demandez
au delegate de rpondre lappel de ladite mthode. Ainsi, on utilise avant lappel de la mthode :

if ([delegate respondsToSelector:@selector(laMethode)]) { [delegate laMethode]; }

RTRACTER

UN CHAMP DE TEXTE

Nous avons besoin de connatre le moment o lutilisateur clique sur le bouton en bas droite du
clavier (Retour) pour valider son entre de texte. Ainsi, nous allons pouvoir rtracter le clavier,
changer la valeur du slider ainsi que celle du label. Implmentons textFieldShouldReturn: dans
RootViewController.m comme suit :

- (BOOL) textFieldShouldReturn:(UITextField *)textField {


if (textField == aTextField) { 
NSString *oatRegEx = @[0-9]{1,}+[0-9.]{0,}; 
NSPredicate *oatTest = [NSPredicate predicateWithFormat:@SELF MATCHES
%@, oatRegEx];
BOOL stringMatchingFloat = [oatTest evaluateWithObject:textField.text]; 
if (stringMatchingFloat) { 
[aSlider setValue:[textField.text oatValue] animated:YES]; 
NSString *stringToPlace = [NSString stringWithFormat:@%.2f,
aSlider.value];
[aLabel setText:stringToPlace]; 
[aTextField setPlaceholder:stringToPlace]; 
[aTextField resignFirstResponder];
}
else {
[aTextField setText:@];

}
}
return YES;
}
 Dans le cas o nous aurions plusieurs champs de texte sur la mme vue, avec des comportements
diffrents, il est ncessaire de les distinguer. Ainsi, nous testons si le champ de texte en paramtre,
celui lorigine de lappel de la mthode, correspond bien aTextField.

01_BlocN_iPhone.indd 56

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

 Pour vrifier que lutilisateur rentre bien un chiffre uniquement, nous allons faire un regex sur le
NSString entr par lutilisateur pour savoir sil contient bien au moins un chiffre entre 0 et 9, puis au
moins un point ou chiffre entre 0 et 9 (pour les chiffres aprs la virgule).
 Le test en question, o nous utilisons NSPredicate.
 Si le texte entr correspond bien un flottant, alors nous passons au  et mettons la valeur du
slider celle entre. Notez que nous choisissons danimer la transition.

Info
Lorsquun string est par exemple @12.4, vous pouvez rcuprer un flottant grce oat aFloat =
[@12.4 oatValue];. Essayez galement intValue, boolValue, ...
 Pour que le tout soit cohrent, il faut changer la valeur du label et mettre la nouvelle valeur du
slider. En , nous mettons le placeHolder du champ de texte (le texte en gris affich avant de saisir
le texte) la valeur du slider galement.
La ligne est trs importante, car elle demande au clavier de se rtracter et donc de disparatre.
Cest la raison pour laquelle nous avons choisi un clavier dit par dfaut. En effet, un clavier numrique ne prsente pas de bouton Retour.

Enfin, si la chane de caractres entre ne correspond pas un flottant, le champ de texte est remis
zro la ligne
, et le clavier ne se rtracte pas.
Toujours dans loptique de rester cohrent, nous devons changer le placeHolder du champ de texte
lorsque nous bougeons le slider. Modifiez ainsi la mthode sliderHandleMethod :

- (void) sliderHandleMethod:(id)sender {
UISlider *theSlider = (UISlider*)sender;
NSString *stringToPlace = [NSString stringWithFormat:@%.2f, theSlider.value];
[aLabel setText:stringToPlace];
[aTextField setPlaceholder:stringToPlace];
}
Encore une fois, compilez et jouez avec votre application !
Pour vous aider, voici quoi devrait ressembler votre classe RootViewController :

RootViewController.h
#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController <UITextFieldDelegate> {
UIButton *aButton;
UISwitch *aSwitch;
UISlider *aSlider;
UILabel *aLabel;
UITextField *aTextField;
}
@end

10

Ragir aux vnements

01_BlocN_iPhone.indd 57

57

18/08/10 12:47

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

10

58

Ragir aux vnements

RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void)viewDidLoad {
aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
aButton.frame = CGRectMake(20.0, 30.0, 120.0, 30.0);
[aButton setTitle:@Touche moi ! forState:UIControlStateNormal];
[aButton setTitle:@Encore ? forState:UIControlStateSelected];
[aButton addTarget:self action:@selector(buttonHandleMethod:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:aButton];
aSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(20.0, 80.0, 94.0, 27.0)];
[aSwitch addTarget:self action:@selector(switchHandleMethod:)
forControlEvents:UIControlEventValueChanged];
[self.view addSubview:aSwitch];
aSlider = [[UISlider alloc] initWithFrame:CGRectMake(20.0, 130.0, 150.0, 20.0)];
aSlider.minimumValue = 0.0;
aSlider.maximumValue = 100.0;
[aSlider addTarget:self action:@selector(sliderHandleMethod:)
forControlEvents:UIControlEventValueChanged];
[self.view addSubview:aSlider];
aLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 170.0, 150.0, 20.0)];
[self.view addSubview:aLabel];
aTextField = [[UITextField alloc] initWithFrame:CGRectMake(20.0, 230.0, 150.0, 30.0)];
[aTextField setBorderStyle:UITextBorderStyleRoundedRect];
[aTextField setPlaceholder:@Entre un chiffre];
[aTextField setDelegate:self];
[aTextField setKeyboardType:UIKeyboardTypeDefault];
[aTextField setClearsOnBeginEditing:YES];
[self.view addSubview:aTextField];
[super viewDidLoad];
}
// Mthode pour grer les vnements du bouton
- (void) buttonHandleMethod:(id)sender {
NSLog(@bouton cliqu !);
UIButton *theButton = (UIButton*)sender;
[theButton setSelected:![theButton isSelected]];
}

01_BlocN_iPhone.indd 58

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// Mthode pour grer les vnements du switch


- (void) switchHandleMethod:(id)sender {
UISwitch *theSwitch = (UISwitch*)sender;
[aButton setSelected:theSwitch.on];
}
// Mthode pour grer les vnements du slider
- (void) sliderHandleMethod:(id)sender {
UISlider *theSlider = (UISlider*)sender;
NSString *stringToPlace = [NSString stringWithFormat:@%.2f, theSlider.value];
[aLabel setText:stringToPlace];
[aTextField setPlaceholder:stringToPlace];
}
// Mthode pour grer les vnements du textField
- (BOOL) textFieldShouldReturn:(UITextField *)textField {
if (textField == aTextField)
{
NSString *oatRegEx = @[0-9]{1,}+[0-9.]{0,};
NSPredicate *oatTest = [NSPredicate predicateWithFormat:@SELF MATCHES
%@, oatRegEx];
BOOL stringMatchingFloat = [oatTest evaluateWithObject:textField.text];
if (stringMatchingFloat) {
[aSlider setValue:[textField.text oatValue] animated:YES];
NSString *stringToPlace = [NSString stringWithFormat:@%.2f,
aSlider.value];

[aLabel setText:stringToPlace];
[aTextField setPlaceholder:stringToPlace];
[aTextField resignFirstResponder];
}
else {
[aTextField setText:@];
}
}
return YES;
}
- (void)dealloc {
[aButton release];
[aSwitch release];
[aSlider release];
[aLabel release];
[aTextField release];
[super dealloc];
}
@end

10

Ragir aux vnements

01_BlocN_iPhone.indd 59

59

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

11

Crer un delegate

60

11 Crer un delegate
Avant de commencer approfondir lutilisation des lments du UIKit, vous devez
comprendre le mcanisme des delegate. Comme toujours, la meilleure manire de
comprendre est de faire !
Le delegate, comprenez gestion des vnements, est un mcanisme particulier, qui se rvle trs utile
lorsque vous avez compris son fonctionnement. Il fait partie des nombreux design patterns (patrons
de conception) comme MVC (Modle Vue Contrleur), KVO KVC (Key Value Observing, Key Value
Coding), les catgories,...
Nous allons crer une classe GestionNotes, qui grera les notes dun lve. Cette classe va implmenter un protocole, qui contiendra des mthodes. Ce protocole sera appel GestionNotesDelegate.
Nous aurons donc une vue de saisie de notes (un UIViewController) qui sera delegate de GestionNotesDelegate.
Dans ce protocole, nous trouverons deux mthodes : une dite requise qui avertira le delegate
lorsquune note aura bien t ajoute dans le tableau et une dite optionnelle avertissant le delegate
quand la moyenne des notes de llve descendra en dessous de 10/20.

DFINITION

DU PROJET

Crons tout dabord un nouveau projet de type Window-based Application que nous appellerons
ApprentissageDelegate. Ajoutez-y un RootViewController comme la Fiche 10 (laissez coch With
XIB for User Interface). Sur la vue de ce contrleur, ajoutez un champ de texte (UITextField), un label
(UILabel) ainsi quun bouton (UIButton) comme la Figure 11.1.

Figure 11.1 :
Construction du
RootViewController.

01_BlocN_iPhone.indd 60

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Code de lapplication delegate :

ApprentissageDelegateAppDelegate.h
#import <UIKit/UIKit.h>
#import RootViewController.h
@interface ApprentissageDelegateAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *rootViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end

ApprentissageDelegateAppDelegate.m
#import ApprentissageDelegateAppDelegate.h
@implementation ApprentissageDelegateAppDelegate
@synthesize window;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
rootViewController = [[RootViewController alloc] init];
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[rootViewController release];
[window release];
[super dealloc];
}
@end
Et le code du RootViewController :

RootViewController.h
#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
UILabel *labelNote;
UIButton *boutonValider;
UITextField *textFieldNote;
}
@end

11

Crer un delegate

01_BlocN_iPhone.indd 61

61

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

11

62

Crer un delegate

RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void)viewDidLoad {
// Label afchant la moyenne
labelNote = [[UILabel alloc] initWithFrame:CGRectMake(20, 30, 200, 30)];
labelNote.text = @pas de notes;
[self.view addSubview:labelNote];
// bouton pour permettre de valider lentre de la note
boutonValider = [UIButton buttonWithType:UIButtonTypeRoundedRect];
boutonValider.frame = CGRectMake(20, 70, 80, 30);
[boutonValider addTarget:self action:@selector(validerSaisieNote:)
forControlEvents:UIControlEventTouchUpInside];
[boutonValider setTitle:@Ajouter forState:UIControlStateNormal];
[self.view addSubview:boutonValider];
// textField permettant de saisir la note
textFieldNote = [[UITextField alloc] initWithFrame:CGRectMake(20, 110, 200, 30)];
textFieldNote.keyboardType = UIKeyboardTypeNumberPad;
textFieldNote.borderStyle = UITextBorderStyleRoundedRect;
textFieldNote.clearsOnBeginEditing = YES;
[self.view addSubview:textFieldNote];
[super viewDidLoad];
}
(void)validerSaisieNote:(id)sender {
// on rentre le clavier
[textFieldNote resignFirstResponder];
}
- (void)dealloc {
[labelNote release];
[boutonValider release];
[textFieldNote release];
[super dealloc];
}
@end

01_BlocN_iPhone.indd 62

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

CRATION DUNE

CLASSE ET DUN PROTOCOLE DLGU

Crons la classe GestionNotes : ajoutez un nouveau fichier (Files > New file puis iPhone OS > Cocoa
Touch Class > Objective-C classe > Subclass of NSObject).
Implmentez-la comme suit (pour linstant, nous ne nous proccupons que du protocole) :

GestionNotes.h
#import <Foundation/Foundation.h>
@protocol GestionNotesDelegate;
@interface GestionNotes : NSObject {
id <GestionNotesDelegate> delegate; 
}
@property (nonatomic, assign) id <GestionNotesDelegate> delegate; 
@end
@protocol GestionNotesDelegate 
@required 
- (void) gestionNotes:(GestionNotes*)gestionNotes aRecuUneNote:(NSInteger)note
nouvelleMoyenne:(oat)moyenne; 
@optional 
- (void) gestionNotes:(GestionNotes*)gestionNotes enDessousMoyenne:(oat)moyenne; 
@end

GestionNotes.m
#import GestionNotes.h
@implementation GestionNotes
@synthesize delegate;
@end
Tout cela parat bien compliqu... Pas dinquitude ! On retrouve, ligne , la dclaration de lobjet
delegate (objet qui GestionNotes va envoyer les vnements).

Info
Id est un pointeur vers nimporte quel type dobjet. La puissance de lObjective-C vient de sa
dynamique, et id en est un exemple. Vous pourriez tout coder avec ce type !
On a le mcanisme des getters/setters cachs derrire @property et @synthesize lignes  et .
Nous spcifions assign, et non pas retain ou copy, pour viter des rfrences circulaires. Quant

11

Crer un delegate

01_BlocN_iPhone.indd 63

63

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

11

64

Crer un delegate

nonatomic, la diffrence datomic, la ressource nest pas bloque (lock) par le thread appelant, ce qui
lui viterait dtre accde par plusieurs threads simultanment.
la ligne  commence la dclaration du protocole GestionNotesDelegate. On retrouve les mots-cls
requis et optionnel ligne  et . Les mthodes qui sont sous le mot-cl @required devront tre
implmentes dans le delegate (ici ), contrairement au mot-cl @optional : les mthodes peuvent
tre implmentes (ici ).

Info
Si vous ne spcifiez rien, ce sera @required par dfaut.

UTILISATION

DE LA CLASSE ET DU PROTOCOLE

Dans la classe RootViewController, faites les changements suivants en partant du code crit jusquici :

RootViewController.h
#import <UIKit/UIKit.h>
#import GestionNotes.h 
@interface RootViewController : UIViewController <GestionNotesDelegate> { 
UILabel *labelNote;
UIButton *boutonValider;
UITextField *textFieldNote;
GestionNotes *gestionnaire; 
}
@end

RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void)viewDidLoad {
// construction de la vue comme au dessus 
// boutonValider, textFieldNote, et labelNote
// gestionnaire des notes
gestionnaire = [[GestionNotes alloc] initWithStudentName:@Thierry]; 
gestionnaire.delegate = self; 
[super viewDidLoad];
}
- (void)validerSaisieNote:(id)sender {
NSInteger note = [textFieldNote.text integerValue];

01_BlocN_iPhone.indd 64

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

if (note <= 20) { 


[gestionnaire ajouterNoteALaMoyenne:note];
[textFieldNote resignFirstResponder];
}
else {
textFieldNote.text = @;
}
}
#pragma mark -

#pragma mark GestionNotes delegate methods


- (void)gestionNotes:(GestionNotes*)gestionNotes aRecuUneNote:(NSInteger)note
nouvelleMoyenne:(oat)moyenne{
[labelNote setText:[NSString stringWithFormat:@moyenne : %f, moyenne]];
}
- (void)gestionNotes:(GestionNotes*)gestionNotes enDessousMoyenne:(oat)moyenne {
UIAlertView *alertV = [[UIAlertView alloc] initWithTitle:@Attention
message:[NSString
stringWithFormat:@Votre moyenne de %f nest pas bonne !, moyenne delegate:self
cancelButtonTitle:@Ok
otherButtonTitles:nil];
[alertV show];
[alertV release];
}
- (void)dealloc {
[labelNote release];
[boutonValider release];
[textFieldNote release];
[gestionnaire release];
[super dealloc];
}
@end
Comme nous avons cr une jolie classe GestionNotes, il faut bien lutiliser ! On commence donc par
un import du .h ligne .
la ligne , on spcifie que la classe RootViewController implmente le protocole GestionNotesDelegate.
On dclare donc un objet de la classe GestionNotes dans le .h ligne  que lon initialisera ligne .
Remarquez que la mthode initWithStudentName:(NSString*)name nest pas encore implmente
dans GestionNotesDelegate. Nous allons y revenir juste aprs.

11

Crer un delegate

01_BlocN_iPhone.indd 65

65

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

11

66

Crer un delegate

Ligne , nous crivons que lobjet de la classe RootViewController, instanci par lapplication delegate est delegate de lobjet gestionnaire.
Noubliez pas qu la ligne  doit se trouver le code qui place les objets, comme dfini plus haut !
Ligne , nous sommes dans la mthode appele en cliquant sur le bouton Ajouter et nous testons si
la note entre est bien infrieure 20 (pour ne pas dpasser 20/20). Si cest le cas, on donne la note
au gestionnaire via la mthode ajouterNoteALaMoyenne:(NSinteger)note qui la placera dans un
tableau, puis on rtracte le clavier. linverse, si la note est suprieure, on efface le champ ligne .
#pragma mark - , que vous retrouvez ligne
est un mot-cl trs utile pour clarifier votre code
lorsque vous souhaitez naviguer rapidement.

Info
#pragma mark - introduit une ligne
#pragma mark GestionNotes delegate methods introduit le texte GestionNotes delegate methods
Vous retrouverez le rsultat Figure 11.2.

Figure 11.2 : Utilisation


de #pragma mark.

La mthode dite requise - (void)gestionNotes:(GestionNotes*)gestionNotes aRecuUneNote:(NSInteger)note nouvelleMoyenne:(oat)moyenne; doit tre implmente pour mettre le label jour, ds
quune note a bien t entre (voir ligne ). Enfin, si la moyenne descend en dessous de 10/20, on
affiche une alert view (sorte de pop up) qui prvient lutilisateur ligne .
Il ne vous aura pas chapp quil manque des choses dans la classe GestionNotes... Finissons de la
construire ensemble !
Rajoutez ces lignes dans le .h :

NSString *studentName;
NSMutableArray *arrayDeNotes;
Ainsi que :
@property (nonatomic, copy) NSString *studentName;
et :

- (id) initWithStudentName:(NSString*)name;
- (void) ajouterNoteALaMoyenne:(NSInteger)note;
Passons au .m maintenant :
#import GestionNotes.h
@interface GestionNotes (PrivateMethods) 
- (oat) calculMoyenne; 
@end

01_BlocN_iPhone.indd 66

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

@implementation GestionNotes
@synthesize delegate;
@synthesize studentName;
- (id) initWithStudentName:(NSString*)name {
if (self = [super init]) 
{
self.studentName = name; 
arrayDeNotes = [[NSMutableArray alloc] init];
}
return self;
}
- (void) ajouterNoteALaMoyenne:(NSInteger)note {
NSNumber *noteNumber = [NSNumber numberWithInteger:note]; 
[arrayDeNotes addObject:noteNumber];
oat moyenne = [self calculMoyenne]; 
if (moyenne < 10.0) {
[delegate gestionNotes:self enDessousMoyenne:moyenne]; 
}
if ([delegate respondsToSelector:@selector(
gestionNotes:aRecuUneNote:nouvelleMoyenne:)]) {
[delegate gestionNotes:self aRecuUneNote:note nouvelleMoyenne:moyenne];

}
}
- (void) dealloc {
[arrayDeNotes release];
[studentName release];
[super dealloc];
}
@end
@implementation GestionNotes (PrivateMethods)
- (oat) calculMoyenne {
NSInteger cumulNotes = 0;
for (NSNumber *number in arrayDeNotes)
{
NSInteger integerValue = [number integerValue];
cumulNotes += integerValue;
}
return (oat)cumulNotes/(oat)[arrayDeNotes count]; 
}
@end

11

Crer un delegate

01_BlocN_iPhone.indd 67

67

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

11

68

Crer un delegate

Ouch ! Pas de panique, buvez frais et nhsitez refaire/relire cette fiche depuis le dbut !
Lorsque vous crivez vos mthodes, il est toujours de bon ton de les rendre prives ou publiques.
Nous ne souhaitons pas que la mthode calculMoyenne ligne  soit accessible, do la syntaxe ligne
 que vous risquez de retrouver trs souvent, avec dautres mots-cls que PrivateMethods. Cest
une des utilisations possibles du patron de conception nomm Catgories.
Nous avons cr notre propre mthode initWithStudentName:(NSString*)name pour vous montrer
la syntaxe que vous devrez respecter pour toutes les mthodes init. Ligne , on appelle init de
super, qui va retourner un objet allou de type id. Comme on a self = [super init], self va faire
rfrence cet objet allou (comprenez : Va pointer sur la mme case mmoire). Enfin, il y a le test if.
En fait, on pourrait expliciter en crivant if((self = [super init]) != nil). Ce qui signifie donc
que ce test sert savoir si linitialisation de super (ici super correspond NSObject) sest bien passe,
et donc que self est bien allou en mmoire.
Si tout va bien, on peut donc continuer notre initialisation en settant la proprit studentName ligne
 et en allouant le tableau qui va nous servir stocker les notes entres.
Les NSArray ou NSMutableArray (mutable sentend pour les objets qui peuvent tre modifis aprs
initialisation) ne peuvent stocker que des objets. Pour ranger nos notes dans ce tableau, qui sont des
NSInteger, il faut donc utiliser NSNumber comme la ligne .

Info
Si vous souhaitez stocker dautres types de donnes, jetez un il du ct de NSValue. Par exemple,
avec une structure que vous avez dfinie :

typedef struct {
NSString *nom;
NSString *prenom
NSInteger age;
} Personne;
Personne unePersonne;
unePersonne.nom = @Dupont;
unePersonne.prenom = @Toto;
unePersonne.age = 27;
Pour encoder en NSValue :
NSValue *aValue = [NSValue value:&unePersonne withObjCType:@encode(Personne)];
Pour dcoder :

Personne personneDecodee;
[aValue getValue:&personneDecodee];
Ensuite, on calcule la nouvelle moyenne de ltudiant ligne  pour vrifier quelle ne descend pas en
dessous de 10/20, auquel cas on appelle la mthode gestionNotes:(GestionNotes*) enDessousMoyenne:(oat)moyenne tout simplement ligne . Cela aura pour effet de dclencher lapparition de lalert
view dans notre rootViewController !

01_BlocN_iPhone.indd 68

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Il faut galement avertir le delegate que la note a bien t ajoute au tableau. Notez que lon pourrait
tester si la note est bien prsente dans le tableau, mais ce nest pas trs utile ici. Rappelez-vous, limplmentation de cette mthode est optionnelle. Ce qui signifie que si le delegate ne limplmente pas,
votre application ne doit pas planter !
Cest pourquoi, ligne , nous testons si le delegate limplmente avec respondsToSelector:. Si cest
le cas, on avertit le delegate ligne
que lobjet gestionnaire a bien reu la note.
Nous avons souhait rendre la mthode calculMoyenne prive. Il faut donc implmenter GestionNotes (PrivateMethods) ligne . Le calcul de la moyenne est trs simple, et nous allons dcouvrir
comment utiliser une boucle For pour faire une numration ligne . En franais dans le texte, cela
donnerait : Pour tous les objets de type NSNumber dans le tableau arrayDeNotes, faire quelque chose.
On calcule donc la moyenne de manire classique, en ajoutant les notes une par une (ligne ) et en
retournant ensuite la somme des notes divise par le nombre total de notes, ligne .
Il ne vous reste plus qu compiler et lancer votre application pour observer son comportement, et
bien comprendre ce que nous avons fait ensemble !
Ce mcanisme de delegate est trs utile et doit tre compris, au mme titre que les eventListener
en Java.

11

Crer un delegate

01_BlocN_iPhone.indd 69

69

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

70

Prsenter des listes de donnes


12 Prsenter des listes de donnes

Vous voil familier avec quelques lments du UIKit, et roi du delegate ! Il existe des
manires lgantes de prsenter des listes de donnes lutilisateur, encore faut-il savoir
les mettre en place.
Maintenant que le delegate na plus de secrets pour vous, nous allons voir comment construire par le
code une table view (liste de donnes) et des picker view (llment rotatif lorsque vous choisissez
lheure de votre rveil par exemple).
Commenons par crer une liste (UITableView). Il ny a rien de plus simple !

UTILISATION DUNE

LISTE DE DONNES

Crez un projet Window-based Application que vous nommerez TableView et ajoutez un fichier, de
type UIViewController > UITableViewController comme la Figure 12.1, que vous nommerez DataListViewController.

Figure 12.1 : Crez un


table view controller.

Dans votre application delegate, ajoutez la window la vue du DataListViewController. Petite piqre
de rappel au besoin :

TableViewAppDelegate.h
#import <UIKit/UIKit.h>
#import DataListViewController.h
@interface TableViewAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
DataListViewController *dataListViewController;
}

01_BlocN_iPhone.indd 70

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

@property (nonatomic, retain) IBOutlet UIWindow *window;


@end

TableViewAppDelegate.m
#import TableViewAppDelegate.h
@implementation TableViewAppDelegate
@synthesize window;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dataListViewController = [[DataListViewController alloc]
initWithStyle:UITableViewStylePlain]; 
[window addSubview:dataListViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[dataListViewController release];
[window release];
[super dealloc];
}
@end
Ligne , nous pouvons remarquer que lon peut choisir le style de la liste de donnes. Il en existe 2 :
plain qui affiche les donnes comme dans le carnet dadresses, ou grouped qui affiche les donnes par
section, comme dans les prfrences de votre iPhone/iPod Touch.
Il faut maintenant afficher des donnes. Dans le .h de DataListViewController, dclarer un NSArray
nomm dataToShow. Dans le viewDidLoad (.m), initialisez-le comme suit :

dataToShow = [[NSArray alloc] initWithObjects:@Element 1, @Element 2,


@Element 3, @Element 4, @Element 5, nil];
Ensuite, modifiez ces quelques lignes de code :
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1; 
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [dataToShow count]; 
}

12

Prsenter des listes de donnes

01_BlocN_iPhone.indd 71

71

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

72

Prsenter des listes de donnes

Ligne , nous spcifions le nombre de sections, comme dans la liste de votre musique dans lapplication iPod (range par ordre alphabtique, chaque lettre correspondant une section). Ici, nous ne
nous en servons pas, il ny a donc quune section. la ligne , cest le nombre de lignes que vous
dfinissez. Ici, nous retournons le nombre dobjets de notre tableau.
Enfin, il faut afficher ces donnes. Pour cela, modifiez la mthode suivante :

- (UITableViewCell *)tableView:(UITableView *)tableView


cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentier:CellIdentier]; 
if (cell == nil) { 
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
cell.textLabel.text = [dataToShow objectAtIndex:[indexPath row]]; 
return cell;
}
Cette mthode porte bien son nom. Elle demande retourner la cellule afficher pour chaque ligne.
Pour que le dfilement de votre table view soit le plus fluide possible, il faut faire trs attention la
gestion mmoire de votre cellule. Par dfaut, Apple introduit un mcanisme que vous retrouvez ligne
 o la cellule est mise en cache en fonction de CellIdentier. Si cette cellule nest pas alloue, on
rentre dans la condition if ligne  pour lallouer. On peut choisir le style de la cellule, nhsitez pas
parcourir la documentation pour les voir tous. Nous apprendrons en crer une personnalise la
section Crer une cellule personnalise quelques pages plus loin. Enfin, nous allons afficher le bon
lment pour la ligne demande.

NSIndexPath comprend deux proprits : section et row (ligne). La cellule de type UITableViewCellStyleDefault comprend entre autres un label textLabel. Nous nous en servons pour afficher ligne 
llment correspondant.
Compilez et lancez.Vous devriez obtenir quelque chose de semblable la Figure 12.2.

01_BlocN_iPhone.indd 72

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 12.2 : Votre premire


liste de donnes !

CLIQUER

SUR UNE CELLULE

Maintenant, nous allons rendre cette liste de donnes cliquable. Pour cela, il faut tout dabord utiliser
un contrleur de navigation (UINavigationController) qui va permettre de grer la navigation entre
les diffrentes vues trs simplement.
Modifiez votre application delegate pour obtenir ceci :

TableViewAppDelegate.h
#import <UIKit/UIKit.h>
#import DataListViewController.h
@interface TableViewAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
DataListViewController *dataListViewController;
UINavigationController *navController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end

TableViewAppDelegate.m
#import TableViewAppDelegate.h
@implementation TableViewAppDelegate
@synthesize window;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

12

Prsenter des listes de donnes

01_BlocN_iPhone.indd 73

73

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

74

Prsenter des listes de donnes

dataListViewController = [[DataListViewController alloc]


initWithStyle:UITableViewStylePlain];

navController = [[UINavigationController alloc]


initWithRootViewController:dataListViewController]; 
[window addSubview:navController.view]; 
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[navController release];
[dataListViewController release];
[window release];
[super dealloc];
}
@end
 On alloue le contrleur de navigation, avec comme rootViewController dataListViewController,
puis on affiche la vue du navController ainsi allou la place de la vue de dataListViewController
ligne .

Ensuite, il faut crer un nouveau contrleur (UIViewController) que vous nommerez DetailListViewController (cochez With XIB for User Interface). Dans ce contrleur, affichez un label, et
dclarez galement un NSString en @property.

DetailListViewController.h
#import <UIKit/UIKit.h>
@interface DetailListViewController : UIViewController {
UILabel *label;
NSString *texteAAfcher;
}
@property (nonatomic, copy) NSString *texteAAfcher;
@end

DetailListViewController.m
#import DetailListViewController.h
@implementation DetailListViewController
@synthesize texteAAfcher;
- (void)viewDidLoad {
label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
[self.view addSubview:label];
label.text = texteAAfcher;
[super viewDidLoad];
}

01_BlocN_iPhone.indd 74

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

- (void)dealloc {
[label release];
[texteAAfcher release];
[super dealloc];
}
@end
Revenez DataListViewController.m et rendez-vous la mthode - (void)tableView:(UITableView *)
tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath que vous implmenterez comme suit :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {
DetailListViewController *detailListViewController = [[DetailListViewController
alloc] initWithNibName:@DetailListViewController bundle:nil];

detailListViewController.texteAAfcher =
[dataToShow objectAtIndex:[indexPath row]];
// possibilit dafcher un titre dans la barre de navigation
detailListViewController.title = [dataToShow objectAtIndex:[indexPath row]];
[self.navigationController pushViewController:detailListViewController
animated:YES];
[detailListViewController release];
}
Maintenant, en cliquant sur une cellule, vous affichez une nouvelle vue. Libre vous de la remplir
comme vous le souhaitez !

Attention
Noubliez pas le #import DetailListViewController.h

CRER

UNE CELLULE PERSONNALISE

Pour finir avec les table view, nous allons voir comment crer une cellule personnalise.
Ajoutez un nouveau fichier, iPhone > Cocoa Touch Classes > Objective-C class > subclass of
UITableViewCell que vous nommerez CustomCell.
Ensuite, modifiez ces nouveaux fichiers pour y ajouter un label :

CustomCell.h
#import <UIKit/UIKit.h>
@interface CustomCell : UITableViewCell {
UILabel *myLabel;
}
@property (nonatomic, retain) UILabel *myLabel;
@end

12

Prsenter des listes de donnes

01_BlocN_iPhone.indd 75

75

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

76

Prsenter des listes de donnes

CustomCell.m
#import CustomCell.h
@implementation CustomCell
@synthesize myLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentier:(NSString *)
reuseIdentier {
if ((self = [super initWithStyle:style reuseIdentier:reuseIdentier]) {
myLabel = [[UILabel alloc] initWithFrame:CGRectMake(150, 5, 120, 30)];
myLabel.font = [UIFont fontWithName:@zapno size:12.0];
[self addSubview:myLabel];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
- (void)dealloc {
[myLabel release];
[super dealloc];
}
@end
Enfin, dans DataListViewController.m, ajouter limport de CustomCell.h, puis modifiez la mthode cellForRowAtIndexPath comme suit :

- (UITableViewCell *)tableView:(UITableView *)tableView


cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @CustomCell;
CustomCell *cell = (CustomCell*)[tableView
dequeueReusableCellWithIdentier:CellIdentier];

if (cell == nil) {
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
cell.myLabel.text = [dataToShow objectAtIndex:[indexPath row]];
return cell;
}
Compilez et lancez, vous devriez obtenir une liste de donnes comme la Figure 12.3.

01_BlocN_iPhone.indd 76

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 12.3 : Une liste de cellules


customises.

Une bonne chose de faite non ? Un petit caf, ou alors continuez sur votre lance !

UTILISER

UN PICKER VIEW OU LISTE CIRCULAIRE

Crez un nouveau projet Window-based Application, que vous nommerez Picker, pour utiliser les
picker view.
Crez ensuite une nouvelle classe de type UIViewController que vous nommerez PickerViewController. Comme dhabitude, ajoutez la vue dun objet PickerViewController (que vous aurez
allou dans votre application delegate) la window, .
Ensuite, dans le viewDidLoad de PickerViewController, ajoutez ceci :

- (void)viewDidLoad {
monPickerView = [[UIPickerView alloc] initWithFrame:CGRectZero]; 
CGSize pickerSize = [monPickerView sizeThatFits:CGSizeZero];
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGRect pickerRect = CGRectMake( 0.0,
screenRect.size.height - 84.0 - pickerSize.height,
pickerSize.width,
pickerSize.height);
monPickerView.frame = pickerRect; 
monPickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

12

Prsenter des listes de donnes

01_BlocN_iPhone.indd 77

77

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

78

Prsenter des listes de donnes


monPickerView.showsSelectionIndicator = YES; 
monPickerView.delegate = self; 
monPickerView.dataSource = self; 
[self.view addSubview:monPickerView];
label = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 200, 20)]; 
[self.view addSubview:label];
pickerViewArray = [[NSArray alloc] initWithObjects:@Element 1, @Element 2,
@Element 3, @Element 4, @Element 5, @Element 6, @Element 7, nil]; 

[super viewDidLoad];
}
La taille dun picker view est optimise. Comprenez que vous ne pouvez pas vraiment le dimensionner
comme vous le souhaitez. Cest pourquoi de la ligne  la ligne , vous trouverez un mcanisme qui
optimise sa taille, et va le centrer sur votre vue.
Vous pouvez choisir dafficher une sorte de vitre, vous montrant la ligne slectionne. Par dfaut, rien
ne saffiche. Ligne , nous choisissons donc de montrer lutilisateur la slection actuelle.
Le picker view ncessite dtre rempli par des donnes, cest pourquoi nous dfinissons le view
controller actuel comme source de donnes ligne , donnes qui seront dans un tableau, initialis
ligne . La classe UIPickerView contient un protocole UIPickerViewDelegate. la ligne , on dfinit
donc self comme tant delegate du picker view.
Pour montrer le bon fonctionnement du picker, on ajoute un label la vue ligne , qui affichera les
lments slectionns.
Comme nous venons de dfinir la classe PickerViewController comme tant la fois source de
donnes et delegate de UIPickerView, vous devez crire ceci dans le .h :

@interface PickerViewController : UIViewController <UIPickerViewDelegate,


UIPickerViewDataSource> {
Ensuite, rappelez-vous, un protocole dfinit des mthodes optionnelles et dautres requises. Vous les
trouverez dans la documentation.
Nous souhaitons afficher deux colonnes dans le picker, une contenant Element 1, Element 2, etc. et
une autre contenant le numro de la ligne.
Ajoutez donc ces mthodes dans PickerViewController.m :
#pragma mark #pragma mark UIPickerViewDelegate
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
label.text = [NSString stringWithFormat:@%@ - %d,
[pickerViewArray objectAtIndex:[pickerView selectedRowInComponent:0]],
[monPickerView selectedRowInComponent:1]]; 

01_BlocN_iPhone.indd 78

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
#pragma mark #pragma mark UIPickerViewDataSource
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
NSString *returnStr = @;
if (component == 0)
{
returnStr = [pickerViewArray objectAtIndex:row]; 
}
else
{
returnStr = [[NSNumber numberWithInt:row] stringValue]; 
}
return returnStr;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger) component
{
CGFloat componentWidth = 0.0;
if (component == 0)
componentWidth = 240.0; 
else
componentWidth = 40.0; 
return componentWidth;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
return 40.0; 
}
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
return [pickerViewArray count]; 
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}

12

Prsenter des listes de donnes

01_BlocN_iPhone.indd 79

79

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

12

80

Prsenter des listes de donnes

Nous choisissons dafficher deux colonnes dans le picker, grce la ligne .


Lignes  et , on doit dfinir la taille pour chaque colonne (nomme ici component, celle numrote
0 tant gauche).
Ligne , on dfinit la hauteur des lignes, et ligne , on dtermine leur nombre qui est dfini par le
nombre dlments du tableau pickerViewArray.
Pour remplir chaque ligne du picker, on implmente la mthode - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component. Pour la
colonne de gauche (0), le titre de la ligne sera llment correspondant dans le tableau, ligne .
Pour celle de droite, le titre le de ligne est son numro, ligne .
Enfin, ligne , on affiche dans le label les lments slectionns dans chaque colonne.

Info
Si vous souhaitez que lutilisateur choisisse une date, il existe un lment pour a : le UIDatePicker

01_BlocN_iPhone.indd 80

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

13

Utiliser une scroll view


13 Utiliser une scroll view

Nous venons de voir comment prsenter de manire efficace des listes de donnes
lutilisateur. Il peut vous arriver davoir afficher un document (image, pdf,...) plus grand
que lcran. Pour cela, il faut utiliser une scroll view.

INTRODUCTION
Pour bien comprendre ce quest une scroll view, jetez tout dabord un il la Figure 13.1.

UIScrollView

UIImageView
(sous-vue de UIScrollView)

Indicateur de
dplacement vertical

Indicateur de
dplacement
horizontal

Les indicateurs de dplacement ne sont


affichs que lorsque lutilisateur bouge le doigt

Nouvelle position de UIImageView

Figure 13.1 : Explication dune scroll view.

Vous remarquez que la taille dune scroll view est plus grande que celle de lcran. Grce aux doigts,
lutilisateur peut la bouger, zoomer, pour afficher la partie qui lintresse sans aucun effort de votre
part, ou presque...
Comme vous pouvez le voir la Figure 13.2, la taille dune scroll view est dfinie par les valeurs
contentSize.width (largeur) et contentSize.height (hauteur). Vous pouvez de plus dfinir des
marges avec contentInset.top (haut), .bottom (bas), .left (gauche) et .right (droite).
Nous allons donc construire une grande scroll view contenant des boutons !
Pour cela, chaque bouton contiendra le numro de la colonne et de la ligne sur lequel il est plac.Vous
obtiendrez ainsi la mme chose que la Figure 13.3.

13

Utiliser une scroll view

01_BlocN_iPhone.indd 81

81

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

13

82

Utiliser une scroll view

contentSize.width

contentInset.top (64px)

Figure 13.2 : Dfinition de


la taille dune scroll view.

contentISize.height

contentInset.botton (44px)

Figure 13.3 : Un nuage


de boutons.

01_BlocN_iPhone.indd 82

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

CRATION

DU PROJET

Crez un projet Window-based Application, que vous nommerez ScrollView, puis ajoutez-y un
RootViewController comme dhabitude, que vous affichez par-dessus la window.
Ensuite, modifiez le RootViewController comme suit :

RootViewController.h
#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController <UIScrollViewDelegate> { 
}
@end
RootViewController.m
#import RootViewController.h

#dene NSLogFunction NSLog(@%s, __FUNCTION__)
#dene
#dene
#dene
#dene
#dene

HAUTEUR_SCROLLVIEW 1000 
LARGEUR_SCROLLVIEW 500
MARGE_PLACEMENT_BOUTONS 10
HAUTEUR_BOUTONS 20
LARGEUR_BOUTONS 50

@implementation RootViewController
- (void)viewDidLoad {
// dnition de la scrollView
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:[[UIScreen
mainScreen] applicationFrame]]; 
scrollView.contentSize = CGSizeMake(LARGEUR_SCROLLVIEW, HAUTEUR_SCROLLVIEW); 
scrollView.delegate = self; 
self.view = scrollView; 
[scrollView release];
// placement des boutons
// calcul du nombre de boutons mettre en largeur
int nbBoutonsLargeur = (int)(LARGEUR_SCROLLVIEW/(LARGEUR_BOUTONS +
MARGE_PLACEMENT_BOUTONS));
// calcul du nombre de boutons mettre en hauteur
int nbBoutonsHauteur = (int)(HAUTEUR_SCROLLVIEW/(HAUTEUR_BOUTONS +
MARGE_PLACEMENT_BOUTONS));
for (int ligne = 0 ; ligne < nbBoutonsHauteur ; ligne++)
for (int colonne = 0 ; colonne < nbBoutonsLargeur ; colonne++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:CGRectMake(colonne*(LARGEUR_BOUTONS +
MARGE_PLACEMENT_BOUTONS), ligne * (HAUTEUR_BOUTONS +
MARGE_PLACEMENT_BOUTONS), LARGEUR_BOUTONS, HAUTEUR_BOUTONS)];

13

Utiliser une scroll view

01_BlocN_iPhone.indd 83

83

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

13

84

Utiliser une scroll view

[button setTitle:[NSString stringWithFormat:@%d%d, ligne, colonne]


forState:UIControlStateNormal];

[button addTarget:self action:@selector(handleClic:)


forControlEvents:UIControlEventTouchUpInside];

button.tag = [button.titleLabel.text intValue];


[self.view addSubview:button];

}
[super viewDidLoad];
}
- (void) handleClic:(id)sender {
UIButton *theSenderButton = (UIButton*)sender;
NSLog(@Sender tag : %d, theSenderButton.tag); 
}
#pragma mark #pragma mark UIScrollView delegate methods
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { 
NSLogFunction;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 
NSLogFunction;
for (UIButton *but in [scrollView subviews])
but.highlighted = YES; 
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)
decelerate { 
NSLogFunction;
for (UIButton *but in [scrollView subviews])
but.highlighted = NO;
}
- (void)dealloc {
[super dealloc];
}
@end
La classe UIScrollView fournit un protocole delegate : UIScrollViewDelegate. Nous choisissons
donc de limplmenter dans la classe RootViewController, do les lignes  et .
Ligne , nous allouons la scroll view, avec comme dimension la taille de lcran (minore de la taille
de la status bar si elle est prsente).
la ligne , on dfinit la taille du contenu de la scroll view. Notez que nous avons dfini ces diffrentes valeurs la ligne  et suivantes. Cela dans le but de rendre le code modifiable trs facilement
si vous souhaitez changer en cours de dveloppement une de ces valeurs.

01_BlocN_iPhone.indd 84

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Nous avons par ailleurs dfini ligne  une petite macro, qui va afficher dans la console [ClasseAppelante nomDeLaMethode], lorsque vous crirez dans votre code NSLogFunction;, [ClasseAppelante
nomDeLaMethode];.
Ligne , nous remplaons la vue par dfaut du RootViewController (self.view) par scrollView.
Notez que lon pourrait lajouter self.view, mais gardez lesprit quil faut toujours minimiser le
nombre de vues empiles. Ensuite, on release scrollView ligne , car self la dtient (avec un
retain).
Pour le besoin de ce tutoriel, nous allons maintenant ajouter des boutons notre scroll view.
Ligne
, grce une double boucle For, et un peu de mathmatiques, les boutons seront placs sur
la scroll view.
Ligne , on dfinit le titre comme tant le numro de la colonne suivi du numro de la ligne.
Chaque bouton aura la mme mthode qui grera le clic, ligne et on utilisera le tag dfini ligne
pour identifier chaque bouton prcisment. Ce tag est en fait le couple colonne/ligne. Dans la mthode
qui rpond lvnement touchUpInside de chaque bouton, handleClic:(id)sender, on affiche dans
la console le numro du tag ligne . Ainsi, on peut savoir directement quel est le bouton qui a envoy
cet vnement.
Regardons maintenant quelques mthodes du delegate.
Ligne , cette mthode sera appele priodiquement, tant que lutilisateur bougera la scroll view.
Ligne , cette mthode sera appele ds lors que lutilisateur, aprs avoir pos son doigt sur la scroll
view, bougera. Pour vous montrer prcisment quel moment cette mthode est appele, on surligne
tous les boutons ligne , et on les rend non surligns dans la mthode ligne  appele lorsque
lutilisateur relche son doigt de la scroll view.
Pour grer le zoom, ajoutez ces quelques lignes : dans le viewDidLoad,

scrollView.maximumZoomScale = 3.0;
scrollView.minimumZoomScale = 1.0;
puis avant // placement des boutons
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@votreImage.png]];
[self.view addSubview:imageView];
Enfin, ajoutez cette mthode delegate la suite des autres :
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return imageView;
}

Info
Pour zoomer avec le simulateur, appuyez sur la touche Alt puis cliquez !

13

Utiliser une scroll view

01_BlocN_iPhone.indd 85

85

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

C HAPITRE 4
UTILISER LES FONCTIONNALITS
DE LIPHONE

01_BlocN_iPhone.indd 87

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Fervent utilisateur de votre iPhone, il ne vous aura pas chapp quil est rempli
de capteurs (GPS, acclromtre, boussole...). Dans ce chapitre, vous allez
apprendre les exploiter, et tirer parti des fonctionnalits comme laccs la
librairie iTunes, lutilisation du Bluetooth, le push...

01_BlocN_iPhone.indd 88

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

Services autour du GPS


14 Services autour du GPS

Une des principales fonctionnalits des smartphones est dintgrer une puce GPS
permettant de localiser lutilisateur. LiPhone ny chappe pas !Voyons ensemble comment
sen servir.

Attention
Vous pourrez compiler sur simulateur pour utiliser le GPS. Toutefois, ce ne sera pas trs simple pour
tester un dplacement par exemple...
De plus, noubliez pas que liPod Touch nintgre pas de puce GPS !
Pour commencer, nous allons faire trs simple : utiliser directement lAPI intgre au SDK et fournie
par Google. Ainsi, nous allons pouvoir afficher la position de lutilisateur sur une carte sans trop
defforts...
Cette carte, un objet de la classe MKMapView, est en ralit plus quune simple carte, car elle affiche la
position de lutilisateur, permet dajouter des annotations (ex. : les endroits favoris de lutilisateur), ou
une vue superpose (overlay view) pour surligner une route par exemple. Elle possde galement un
objet de la classe MKUserLocation, si vous souhaitez rcuprer des donnes comme laltitude, la
vitesse, la position en coordonnes GPS, la prcision de la localisation...
Comme toujours, crez un nouveau projet que vous appellerez GPS, et ajoutez-y un RootViewController que vous afficherez par-dessus la window.Vous allez finir par le faire les yeux ferms...

AFFICHER

LA POSITION DE LUTILISATEUR SUR UNE CARTE

Une fois tout ceci en place, ajoutez le framework MKMapKit, et crivez ce code :

RootViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h> 
@interface RootViewController : UIViewController <MKReverseGeocoderDelegate,
MKMapViewDelegate> {
MKMapView *maMapView; 
UILabel *labelReverseGeoCoder;
UIButton *buttonLaunchReverseGeocode;
UIActivityIndicatorView *activityReverseGeoCod;
MKReverseGeocoder *reverseGeocoder; 
}
@end

RootViewController.m
#import RootViewController.h
@implementation RootViewController

14

Services autour du GPS

01_BlocN_iPhone.indd 89

89

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

90

Services autour du GPS

- (void)viewDidLoad {
// initialisation de la carte
maMapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, 320, 360)];
// afchage de la position de lutilisateur
maMapView.showsUserLocation = YES; 
// on change le type dafchage pour mettre carte + satellite
maMapView.mapType = MKMapTypeHybrid; 
// self recevra les vnements de maMapView
maMapView.delegate = self;
[self.view addSubview:maMapView];
// dclaration du label afchant ladresse actuelle de lutilisateur
labelReverseGeoCoder = [[UILabel alloc] initWithFrame:CGRectMake(20, 380, 280, 40)];
[self.view addSubview:labelReverseGeoCoder];
// dclaration du bouton qui va servir lancer le reverse geo coder
buttonLaunchReverseGeocode = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonLaunchReverseGeocode.frame = CGRectMake(20, 420, 150, 30);
[buttonLaunchReverseGeocode setTitle:@Donne mon adresse
forState:UIControlStateNormal];
[buttonLaunchReverseGeocode addTarget:self
action:@selector(reverseGeocodeCurrentLocation:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonLaunchReverseGeocode];
// dclaration de la roue qui indique une activit
activityReverseGeoCod = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityReverseGeoCod.hidesWhenStopped = YES;
activityReverseGeoCod.center = CGPointMake(260, 430);
[self.view addSubview:activityReverseGeoCod];
[super viewDidLoad];
}
#pragma mark #pragma mark button methods
- (void)reverseGeocodeCurrentLocation : (id) sender 
{
// test de la prcision
if(maMapView.userLocation.location.horizontalAccuracy > 100.0)
// distance en mtres 
{
//prcision insufsante, on afche une alert view

01_BlocN_iPhone.indd 90

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

UIAlertView *monAlert = [[UIAlertView alloc]


initWithTitle:@Erreur
message:[NSString stringWithFormat:@La prcision de %.2f m est
insufsante, maMapView.userLocation.location.horizontalAccuracy]
delegate:self
cancelButtonTitle:@Ok
otherButtonTitles:nil];
[monAlert show];
[monAlert release];
}
else
{
// la prcision est sufsante, on demande faire un reverse
[activityReverseGeoCod startAnimating];
// allocation du reverse geocoder avec les coordonnes actuelles de
lutilisateur
reverseGeocoder = [[MKReverseGeocoder alloc]
initWithCoordinate:maMapView.userLocation.location.coordinate];
reverseGeocoder.delegate = self;
[reverseGeocoder start];
}
}
#pragma mark #pragma mark MKMapView delegate
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)
userLocation
{
// on zoome
MKCoordinateSpan span = {0.01, 0.01};

[mapView setRegion:MKCoordinateRegionMake(mapView.userLocation.location
.coordinate, span) animated:YES];
}
#pragma mark #pragma mark Reverse geocoder delegate
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
{
NSLog(@MKReverseGeocoder has failed : %@, [error localizedDescription]);
[activityReverseGeoCod stopAnimating];
// release si erreur
[geocoder release];
}
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark
*)placemark
{

14

Services autour du GPS

01_BlocN_iPhone.indd 91

91

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

92

Services autour du GPS

labelReverseGeoCoder.text = [NSString stringWithFormat:@%@ %@ %@ %@,


placemark.country, placemark.postalCode, placemark.locality,
placemark.thoroughfare]; 

[labelReverseGeoCoder sizeToFit];
[activityReverseGeoCod stopAnimating];
// on a ni dutiliser le reverseGeocoder, que lon alloue chaque clic sur le
bouton, donc on le release ici
[geocoder release]; 
}
(void)dealloc {
if(reverseGeocoder)
[reverseGeocoder release];
[maMapView release];
[activityReverseGeoCod release];
[labelReverseGeoCoder release];
[buttonLaunchReverseGeocode release];
[super dealloc];
}
@end
Comme nous avons ajout un framework notre application, le MapKit, il faut faire limport de la
ligne  !
Ligne , un objet de type MKMapView est dclar, il reprsente la carte fournie par Google.
Ligne , on dclare un objet MKReverseGeocoder qui va nous permettre de retrouver ladresse (pays,
ville, code postal, numro de rue, nom de la rue) partir des coordonnes GPS de lutilisateur !
Passons maintenant limplmentation : comme indiqu ligne , nous pouvons demander linstance
de MKMapView de montrer la position de lutilisateur. Cela signifie que sa position saffichera laide du
point bleu, comme dans lapplication Plans de liPhone, et que laffichage de cette position sera automatiquement mis jour lors du dplacement.

Info
MKMapView gre donc son propre Location Manager (lobjet qui se charge de dialoguer avec le GPS).
Ainsi, on peut accder lobjet de type CLLocation que vous retrouvez avec maMapView.userLocation.
location. Nous allons apprendre nous en servir plus loin dans cette fiche.
Ligne , on peut galement choisir le type daffichage de la carte : cartographie, satellite, ou hybride
(les deux en mme temps). Notez quen slectionnant satellite seulement, vous risquez de ne pas
avoir dimages si vous zoomez trop.
On choisit dappeler une mthode qui va nous permettre de lancer le reverse geocoding avec un bouton.
Cette mthode est implmente ligne , mais pour obtenir un rsultat correct, il faut un minimum de
prcision sur la mesure. Cest pourquoi nous choisissons de tester la prcision horizontale actuelle
(horizontalAccuracy pour la prcision sur la mesure des coordonnes GPS, et donc la position sur la
terre, verticalAccuracy pour la prcision sur la mesure de laltitude). Si cette prcision est infrieure
100 mtres, on choisit davertir lutilisateur et de ne pas lancer le reverse geocoding ligne .

01_BlocN_iPhone.indd 92

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ligne , lorsque la prcision est infrieure 100 mtres, on peut faire appel au reverse geocoder en lui
fournissant les coordonnes GPS actuelles de lutilisateur. On alloue ici lobjet reverseGeocoder, que
lon libre ensuite lorsquil a fait son travail ligne ou . Pour viter tout problme, on libre lobjet
geocoder pass en paramtre des mthodes delegate, qui est une rfrence vers reverseGeocoder.

Info
Vous pouvez donc retrouver nimporte quelle adresse du moment que vous avez les coordonnes
GPS ! Il suffit de crer une variable de type CLLocationCoordinate2D, et lui spcifier la latitude ainsi
que la longitude.
Passons la mthode delegate de MKMapView. Ligne , on choisit de zoomer sur la carte avec animation. La valeur de ce zoom est dfinie la ligne
avec la variable span de type MKCoordinateSpan.
Dans la documentation :

typedef struct {
CLLocationDegrees latitudeDelta;
CLLocationDegrees longitudeDelta;
} MKCoordinateSpan;
Ainsi, on choisit le delta en latitude et le delta en longitude afficher sur la carte. Ces valeurs sont
exprimes en degrs.

Rappel
43.4035 correspond 432412 (0,4035 * 60 = 24,21 24 ; 0,21 * 60 = 12,6 12)
234504 correspond 23,7511 (23 + 45/60 + 4/3600)
Comme toute application proprement implmente, les mthodes de gestion des erreurs et exceptions sont primordiales. Et parce quil est impossible de prdire le comportement de lapplication
sur tous les iPhones en circulation, noubliez pas dimplmenter (ou crer) aussi souvent que
possible de telles mthodes. Ligne , on se sert dune mthode trs pratique de NSError : localizedDescription pour afficher la description de lerreur plutt quun vulgaire numro derreur.
Nhsitez pas essayer en coupant votre connexion Internet par exemple !
Enfin, ligne , on affiche dans le label les diffrentes informations rendues par le reverse geocoder.
Quelques explications : imaginez que vous soyez devant le 4, avenue du Prado, 13006 Marseille. Voici
les informations correspondantes :

14

administrativeArea : Provence-Alpes-Cte dAzur


country : France
countryCode : FR
locality : Marseille
postalCode : 13006
subAdministrativeArea : Bouches-du-Rhne
subThoroughfare : 4
thoroughfare : Avenue du Prado

Services autour du GPS

01_BlocN_iPhone.indd 93

93

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

94

Services autour du GPS

EXERCICE
Pour vous entraner, mettez deux boutons sur lalerte relative la prcision. Cela permettra
lutilisateur de lancer quand mme le reverse geocoding sil le souhaite.

SOLUTION
Modifier la mthode appele lorsque vous cliquez sur le bouton :

- (void)reverseGeocodeCurrentLocation : (id) sender


{
// test de la prcision
if(maMapView.userLocation.location.horizontalAccuracy > 100.0) // distance en mtres
{
//prcision insufsante, on afche une alert view
UIAlertView *monAlert = [[UIAlertView alloc]
initWithTitle:@Erreur
message:[NSString stringWithFormat:@La prcision de %.2f m est insufsante,
maMapView.userLocation.location.horizontalAccuracy]
delegate:self
cancelButtonTitle:@Annuler
otherButtonTitles:@Continuer, nil];
[monAlert show];
[monAlert release];
}
et ajouter cette mthode :

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex


{
if(buttonIndex == 1)
{
// lutilisateur a cliqu sur continuer
[activityReverseGeoCod startAnimating];
// allocation du reverse geocoder avec les coordonnes actuelles de lutilisateur
reverseGeocoder = [[MKReverseGeocoder alloc]
initWithCoordinate:maMapView.userLocation.location.coordinate];
reverseGeocoder.delegate = self;
[reverseGeocoder start];
}
}

01_BlocN_iPhone.indd 94

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

AFFICHER

DES ANNOTATIONS SUR LA CARTE

Sur la carte, il est possible dafficher des annotations, qui sont par dfaut des petites punaises places
sur la carte. En fait, vous pouvez changer pour mettre votre propre ressource graphique.
Lajout des annotations personnalises se fait en crant une classe qui respecte le protocole MKAnnotation. Les faons doprer sont nombreuses, nous suivons celle-ci : on peut crer une classe conforme
au protocole MKAnnotationView pour dessiner ce que lon souhaite, mais il faut de toute manire
implmenter - (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id
<MKAnnotation>)annotation.
Commenons par les annotations : crez un fichier Objective-C class subclass of NSObject que vous
nommerez MonAnnotation. Implmentez-le comme ceci :

MonAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface MonAnnotation :NSObject <MKAnnotation> { 
// pour tre conforme au protocole MKAnnotation
NSString *title;
NSString *subtitle;
// requis par le protocole
CLLocationCoordinate2D coordinate; 
// pour ne pas importer le framework CoreLocation
NSNumber *latitude;
NSNumber *longitude;

}
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *subtitle;
@property (nonatomic, retain) NSNumber *latitude;
@property (nonatomic, retain) NSNumber *longitude;
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@end
MonAnnotation.m
#import MonAnnotation.h
@implementation MonAnnotation
@synthesize title, subtitle, coordinate, latitude, longitude;
- (CLLocationCoordinate2D)coordinate 
{
coordinate.latitude = [self.latitude doubleValue];
coordinate.longitude = [self.longitude doubleValue];
return coordinate;
}
@end

14

Services autour du GPS

01_BlocN_iPhone.indd 95

95

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

96

Services autour du GPS

Lorsque lon cre cette classe, qui doit respecter le protocole MKAnnotation spcifi la ligne , on
doit implmenter certaines proprits comme la ligne . Rien ne vous empche dajouter dautres
variables partir de la ligne , comme une proprit de description.
Enfin, pour ne pas avoir inclure le framework CoreLocation, on dfinit une mthode ligne  qui
construira une variable de type CLLocationCoordinate2D partir de deux objets NSNumber : latitude
et longitude. Ceci pour viter de solliciter CLLocationCoordinate2DMake ncessitant ledit framework.
Ensuite, ajoutez cette mthode dans le .m de RootViewController, vers les mthodes delegate de
MKMapView pour personnaliser notre punaise sur la carte !

- (MKAnnotationView *)mapView:(MKMapView *)theMapView


viewForAnnotation:(id <MKAnnotation>)annotation
{
// si cest la position de lutilisateur, on laisse tel quel
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// On teste nos types dannotations. Ici cest inutile car on en a quune,
mais au moins vous saurez le faire !

if ([annotation isKindOfClass:[MonAnnotation class]])


{
//On essaye de dpiler une annotation view en premier
static NSString* MonAnnotationIdentier = @monAnnotationIdentier;
MKPinAnnotationView* pinView = (MKPinAnnotationView *)[maMapView
dequeueReusableAnnotationViewWithIdentier:MonAnnotationIdentier];
if (!pinView)
{
// Si il ny en avait aucune de disponible, on en cre une tout simplement !
MKPinAnnotationView* customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation
reuseIdentier:MonAnnotationIdentier] autorelease];
customPinView.pinColor = MKPinAnnotationColorGreen;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// On ajoute une petite che sur le ct pour afcher plus
dinformations en cliquant dessus

//
// Info : on pourrait implmenter directement :
// - (void)mapView:(MKMapView *)mapView annotationView:( MKAnnotationView *)
view calloutAccessoryControlTapped:(UIControl *)control;
//Lavantage ici est de choisir ce que lon veut mettre
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:@selector(afcherLesDetails:)
forControlEvents:UIControlEventTouchUpInside];

01_BlocN_iPhone.indd 96

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

customPinView.rightCalloutAccessoryView = rightButton;
// ajout dune image gauche
UIImageView *imageView =
[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
imageView.image = [UIImage imageNamed:@annotationImage.png];
customPinView.leftCalloutAccessoryView = imageView;
[imageView release];
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Mais il faut galement les ajouter sur la carte... Dans RootViewController.h, dclarer un NSMutableArray *arrayOfAnnotations;, faites limport de MonAnnotation.h et rajouter ces quelques
lignes dans le viewDidLoad dans le .m :

// Ajout des annotations


arrayOfAnnotations = [[NSMutableArray alloc] initWithCapacity:2];
MonAnnotation *uneAnnotation = [[MonAnnotation alloc] init];
uneAnnotation.title = @Paris;
uneAnnotation.subtitle = @Capitale de la france;
uneAnnotation.longitude = [NSNumber numberWithDouble:2.0+20.0/60.0];
uneAnnotation.latitude = [NSNumber numberWithDouble:48.0+52.0/60.0];
[arrayOfAnnotations addObject:uneAnnotation];
[uneAnnotation release];
uneAnnotation = [[MonAnnotation alloc] init];
uneAnnotation.title = @Marseille;
uneAnnotation.subtitle = @Ville du sud;
uneAnnotation.longitude = [NSNumber numberWithDouble:5.0+22.0/60.0];
uneAnnotation.latitude = [NSNumber numberWithDouble:43.0+17.0/60.0];
[arrayOfAnnotations addObject:uneAnnotation];
[uneAnnotation release];
[maMapView addAnnotations:arrayOfAnnotations];

14

Services autour du GPS

01_BlocN_iPhone.indd 97

97

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

98

Services autour du GPS

Ajoutez galement cette mthode :

- (void) afcherLesDetails:(id)sender
{
NSLog(@ici, on pourrait prsenter un controller qui afcherait le dtail);
}
Voici ce que vous devriez obtenir Figure 14.1.

Figure 14.1 : Une annotation


de type pin personnalise.

Pour finir, nous allons compltement choisir ce que lon souhaite afficher sur la carte... Dans ce but,
crez une nouvelle classe que vous nommerez MonAnnotationView, et implmentez-la comme suit :

MonAnnotationView.h
#import <MapKit/MapKit.h>
@interface MonAnnotationView : MKAnnotationView
{
}
@end
MonAnnotationView.m
#import MonAnnotationView.h
@implementation MonAnnotationView
- (id)initWithAnnotation:(id <MKAnnotation>)annotation
reuseIdentier:(NSString *)reuseIdentier

01_BlocN_iPhone.indd 98

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

{
self = [super initWithAnnotation:annotation reuseIdentier:reuseIdentier];
if (self != nil)
{
UIImageView *view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
view.image = [UIImage imageNamed:@annotationImage.png];
[self addSubview:view];
[view release];
}
return self;
}
- (void)setAnnotation:(id <MKAnnotation>)annotation
{
[super setAnnotation:annotation];
// pour updater si besoin lafchage lors que lon utilise reuseIdentier
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
// implmenter si besoin de dessiner
}
@end
Et bien entendu, il faut modifier la mthode du RootViewController.m (ne pas oublier limport de
MonAnnotationView.m) :

- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id


<MKAnnotation>)annotation
{
// si cest la position de lutilisateur, on laisse tel quel
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// On teste nos types dannotations. Ici cest inutile car on en a quune,
mais au moins vous saurez le faire !

if ([annotation isKindOfClass:[MonAnnotation class]])


{
//On essaye de dpiler une annotation view en premier
static NSString* MonAnnotationIdentier = @monAnnotationIdentier;
MonAnnotationView* pinView = (MonAnnotationView *)[maMapView
dequeueReusableAnnotationViewWithIdentier:MonAnnotationIdentier];
if (!pinView)
{

14

Services autour du GPS

01_BlocN_iPhone.indd 99

99

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

100

Services autour du GPS

// Sil ny en avait aucune de disponible, on en cre une tout simplement !


MonAnnotationView* customView = [[[MonAnnotationView alloc]
initWithAnnotation:annotation reuseIdentier:MonAnnotationIdentier]
autorelease];
customView.canShowCallout = YES;
return customView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
Le rsultat est illustr Figure 14.2.
Ouf... Ctait facile mais long ! Avant liOS 3.0, cette carte
nexistait pas. Il fallait donc employer un autre framework :
CoreLocation. Il se rvlera trs pratique lorsque vous
aurez besoin dutiliser les coordonnes GPS de lutilisateur sans pour autant afficher sa position sur une carte.
Pour la petite histoire, une des solutions retenues pour
afficher la position de lutilisateur tait de recourir une
WebView affichant le site de Google Maps. Ce ntait pas
trs pratique ! Cependant, ne ngligez pas la suite de
cette fiche !

UTILISER

LE FRAMEWORK

Figure 14.2 : Une annotation


compltement personnalise.

CORELOCATION

POUR RCUPRER LA POSITION


Crez comme toujours un nouveau projet Windows-based Application que nous appellerons GPS2
(trs cratif comme nom !) et ajoutez-y une classe RootViewController. Ensuite, ajoutez le framework
CoreLocation.
Avec ce framework, nous allons crer un objet de la classe CLLocationManager. Ainsi, nous allons
pouvoir utiliser la fois la puce GPS, et la boussole ! Regardons le code de plus prs :

RootViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@interface RootViewController : UIViewController <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
BOOL isCompassAvailable;

01_BlocN_iPhone.indd 100

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

UITextView *textViewDonneesGPS;
UILabel *labelDonneesHeading;
}
@end
RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void)viewDidLoad {
// ------------------------------------------------// Conguration du location Manager
// ------------------------------------------------// on alloue un Manager pour la location
locationManager = [[CLLocationManager alloc] init];
// self est le delegate
locationManager.delegate = self;
// Par dfaut, on met la meilleure localisation
locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
// on update autant que possible
locationManager.distanceFilter = kCLDistanceFilterNone; 
// On vrie que le device a bien la boussole de dispo
if ([CLLocationManager headingAvailable] == NO) { 
// pour iPhone OS < 4.0 : if (locationManager.headingAvailable == NO)
// pas de boussole disponible
isCompassAvailable = NO;
} else {
isCompassAvailable = YES;
// conguration du ltre pour la boussole
locationManager.headingFilter = kCLHeadingFilterNone;
}
// ------------------------------------------------// Elements graphiques
// ------------------------------------------------textViewDonneesGPS = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
textViewDonneesGPS.editable = NO; 
[self.view addSubview:textViewDonneesGPS];
labelDonneesHeading = [[UILabel alloc] initWithFrame:CGRectMake(0, 220, 320, 30)];
// multi lignes
labelDonneesHeading.numberOfLines = 0; 
[self.view addSubview:labelDonneesHeading];

14

Services autour du GPS

01_BlocN_iPhone.indd 101

101

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

102

Services autour du GPS

// ------------------------------------------------// Dmarrage localisation + boussole


// ------------------------------------------------// on lance la localisation
[locationManager startUpdatingLocation]; 
// on lance la boussole
[locationManager startUpdatingHeading]; 
[super viewDidLoad];
}
#pragma mark #pragma mark location delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation
*)newLocation fromLocation:(CLLocation *) oldLocation {
NSMutableString *stringToDisplay = [NSMutableString string];

[stringToDisplay appendFormat:@lat : %f long : %f\n,


newLocation.coordinate.latitude, newLocation.coordinate.longitude];
[stringToDisplay appendFormat:@altitude : %f\n, newLocation.altitude];
[stringToDisplay appendFormat:@vitesse : %f m/s \n, newLocation.speed];
[stringToDisplay appendFormat:@prcision horiz : %f, vert : %f\n,
newLocation.horizontalAccuracy, newLocation.verticalAccuracy];
textViewDonneesGPS.text = stringToDisplay;
}
#pragma mark #pragma mark Delegate de la boussole(heading)
- (void)locationManager:(CLLocationManager *)manager
didUpdateHeading:(CLHeading *)heading {
labelDonneesHeading.text = [NSString stringWithFormat:@%f ; x : %f, y : %f,
z: %f, heading.trueHeading, heading.x, heading.y, heading.z];

[labelDonneesHeading sizeToFit];

}
#pragma mark #pragma mark Gestion des erreurs
// Cette mthode est appele lorsque lobjet actualisant la localisation
rencontre une erreur
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError
*)error { 
if ([error code] == kCLErrorDenied) { 

01_BlocN_iPhone.indd 102

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// Cette erreur indique que lutilisateur a refus laccs au service de


localisation

[locationManager stopUpdatingHeading];
[locationManager stopUpdatingLocation];
} else if ([error code] == kCLErrorHeadingFailure) { 
// Cette erreur indique que les donnes de la boussole ne peuvent tre
dtermines, probablement cause dinterfrences
}
}
- (void)dealloc {
[textViewDonneesGPS release];
[labelDonneesHeading release];
[locationManager stopUpdatingHeading];
[locationManager stopUpdatingLocation];
[locationManager release];
[super dealloc];
}
@end
Cela fait beaucoup en une fois, mais analysons pas pas :
Ligne  : on peut spcifier la prcision pour la mesure GPS. Sachez que plus fine sera la prcision
demande, plus frquents seront les appels la puce GPS, et donc plus votre application consommera
de batterie. Tomber en panne alors que lon est perdu serait le comble ! Prennez donc un temps de
rflexion avant de choisir kCLLocationAccuracyBest...
Ligne , vous dfinissez la distance minimale de dplacement en mtre atteindre, avant davertir le
delegate. En mettant kCLDistanceFilterNone, le plus petit mouvement sera dtect. En mettant 3.0,
le delegate serait averti tous les 3 mtres. (Pour la boussole, le filtre intervient sur les degrs de rotation.)
LiPhone 3G et liPod Touch nont pas de boussole disponible. Avant de lutiliser dans votre application,
il faut donc tester si lappareil qui lance votre application possde ce capteur. Cest ce qui est crit
ligne . Notez quavant liOS 4.0, on accdait la proprit headingAvailable (BOOL) du locationManager, maintenant remplace par une mthode de classe.
Pour afficher toutes ces donnes, nous allons utiliser une text view (pour les donnes GPS) et un label
(pour les donnes du magntomtre). Tous deux pourront afficher plusieurs lignes.
Ligne , on choisit de rendre la text view non ditable. Pour rendre un label multiligne, il faut lui
spcifier... 0 ligne ! Cest ce qui est fait ligne . Ainsi, le label va automatiquement afficher les donnes
en crant plusieurs lignes aprs appel de sizeToFit ligne . Nos lments dfinis et initialiss, il est
temps de lancer les mises jour de position (GPS + boussole) ligne  et.

14

Services autour du GPS

01_BlocN_iPhone.indd 103

103

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

14

104

Services autour du GPS

Il faut un delegate pour se servir correctement de la classe CLLocationManager. La mthode la plus


importante implmenter est celle de la ligne . En effet, elle renseigne le delegate de la position de
lutilisateur selon les donnes spcifies lignes  et .
Dans cette mthode, nous allons afficher les diffrentes ressources mises votre disposition. Pour
cela, on initialise un NSMutableString ligne
, puis on ajoute petit petit au string les valeurs de
laltitude en mtre, la vitesse en mtres par secondes, les coordonnes GPS (latitude longitude) en
degrs, les prcisions verticale et horizontale en mtres.

Info
Lorsquun mutable string (un string modifiable) unString vaut @Hello , puis que vous faites
[unString appendString:@iPuP];, il vaudra @Hello iPuP. appendFormat est lquivalent de

appendString:[NSString stringWithFormat:]
Ligne , on implmente selon le mme principe la mthode delegate pour la boussole, appele
chaque fois que vous effectuez une rotation de langle minimum spcifi linitialisation. Cette fois-ci,
on rempli le label directement ligne , avec notamment la valeur en degr de votre position par
rapport au vrai nord (on peut choisir le nord magntique).
Pour finir, comme dit prcdemment dans cette fiche, la gestion des erreurs est primordiale (lignes
,  et ).

Info
Avec larrive de liOS 4.0, lutilisateur peut activer ou non les services de localisation dans les
prfrences de liPhone (notamment pour viter davoir des applications exploitant votre position
en tche de fond). Vous devrez donc tester si lutilisateur a ou non autoris ces services avec
[CLLocationManager locationServicesEnabled];. Si la valeur retourne est non et que vous
dmarrez tout de mme la localisation, une alert view saffichera, demandant lutilisateur sil
souhaite ractiver ce service.

01_BlocN_iPhone.indd 104

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

15

Jouer de la musique en secouant


15 Jouer de la musique en secouant

Votre iPhone est galement un iPod. Apple autorise laccs la bibliothque de lutilisateur
en lecture : impossible de lcrire (modifier les informations dun morceau par exemple).
En plus de jouer de la musique, nous verrons comment interagir avec lacclromtre.
Nous allons accder la bibliothque : crez un nouveau projet Window-based Application que vous
nommerez ShakeToPlay, et ajoutez un RootViewController. Ensuite, ajoutez le framework MediaPlayer.

Info
La bibliothque de liPod nest pas accessible sur simulateur, il vous faudra donc compiler sur iPhone,
iPod Touch ou iPad. Il en va de mme pour lacclromtre !

JOUER

LA MUSIQUE DE LIPOD

Dans un premier temps, affichons un bouton Play/Pause ainsi quun autre pour slectionner les
morceaux :

RootViewController.h
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
@interface RootViewController : UIViewController <MPMediaPickerControllerDelegate> {

MPMusicPlayerController *musicPlayer;
UIButton *playPauseButton;
}
- (void) registerForMediaPlayerNotications;
@end
RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void) registerForMediaPlayerNotications {
NSNoticationCenter *noticationCenter = [NSNoticationCenter defaultCenter];
[noticationCenter addObserver: self
selector: @selector(gerer_LelementJoueEnCoursAChange:)
name: MPMusicPlayerControllerNowPlayingItemDidChangeNotication
object: musicPlayer]; 
[noticationCenter addObserver: self
selector: @selector(gerer_LetatPlayPauseAChange:)
name: MPMusicPlayerControllerPlaybackStateDidChangeNotication
object: musicPlayer];

15

Jouer de la musique en secouant

01_BlocN_iPhone.indd 105

105

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

15

106

Jouer de la musique en secouant

[musicPlayer beginGeneratingPlaybackNotications];
}
- (void)viewDidLoad {
// bouton pour lancer la slection dune chanson
UIButton *mediaPickerButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
mediaPickerButton.center = CGPointMake(20, 20);
[mediaPickerButton addTarget:self action:@selector(selectASong:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:mediaPickerButton];
// Gestion de la lecture de la musique
// bouton qui gre le play pause
playPauseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[playPauseButton setTitle:@Play forState:UIControlStateNormal];
[playPauseButton setTitle:@Pause forState:UIControlStateSelected];
[playPauseButton addTarget:self action:@selector(playOrPauseMusic:)
forControlEvents:UIControlEventTouchUpInside];
[playPauseButton setFrame:CGRectMake(10, 50, 50, 30)];
// on empche lutilisateur de cliquer dessus tant quil na pas slectionn
de musique
playPauseButton.enabled = NO; 
[self.view addSubview:playPauseButton];
// on initialise le music player
musicPlayer = [[MPMusicPlayerController applicationMusicPlayer] retain]; 
// on lance labonnement aux notications
[self registerForMediaPlayerNotications]; 
[super viewDidLoad];
}
#pragma mark #pragma mark button Methods
- (void) selectASong:(id)sender
{
// initialisation et afchage du picker pour choisir la musique
MPMediaPickerController *picker = [[MPMediaPickerController alloc]
initWithMediaTypes: MPMediaTypeMusic]; 
picker.delegate = self;
picker.allowsPickingMultipleItems = YES; 
picker.prompt = @Choisir votre liste de lecture;

01_BlocN_iPhone.indd 106

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[self presentModalViewController: picker animated: YES];


[picker release];
}
- (void) playOrPauseMusic:(id)sender {
MPMusicPlaybackState playbackState = [musicPlayer playbackState];
if (playbackState == MPMusicPlaybackStateStopped || playbackState ==
MPMusicPlaybackStatePaused) {

[musicPlayer play];
} else if (playbackState == MPMusicPlaybackStatePlaying) {
[musicPlayer pause];
}
}
#pragma mark #pragma mark MPMediaPickerControllerDelegate methods
- (void)mediaPicker: (MPMediaPickerController *)mediaPicker
didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
// on vient de choisir une chanson, on autorise le bouton play/pause
playPauseButton.enabled = YES;
BOOL wasPlaying = ([musicPlayer playbackState] ==
MPMusicPlaybackStatePlaying);

[musicPlayer setQueueWithItemCollection: mediaItemCollection];

// on joue le morceau si on ntait pas en pause


if (wasPlaying) {
[musicPlayer play];
}
[mediaPicker dismissModalViewControllerAnimated:YES];
}
- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker
{
[mediaPicker dismissModalViewControllerAnimated:YES];
}
#pragma mark #pragma mark gestionnaires notications Musique
- (void) gerer_LelementJoueEnCoursAChange: (id) notication {
}
// Lorsque ltat du player change, on met jour le bouton play/pause
- (void) gerer_LetatPlayPauseAChange: (id) notication {

15

Jouer de la musique en secouant

01_BlocN_iPhone.indd 107

107

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

15

108

Jouer de la musique en secouant

MPMusicPlaybackState playbackState = [musicPlayer playbackState];


playPauseButton.enabled = (playbackState != MPMusicPlaybackStateStopped);
[playPauseButton setSelected:(playbackState == MPMusicPlaybackStatePlaying)]; 
if (playbackState == MPMusicPlaybackStateStopped) {
// On dsactive le bouton
[playPauseButton setEnabled:NO];
// Mme si le lecteur tait arrt, on stoppe quand mme pour tre sr
de vider le buffer
[musicPlayer stop];
}
}
- (void)dealloc {
[musicPlayer stop];
[musicPlayer release];
NSNoticationCenter *noticationCenter = [NSNoticationCenter defaultCenter];
[noticationCenter removeObserver:self];
[playPauseButton release];
[super dealloc];
}
@end
Lignes  et  : pour jouer de la musique, nous avons besoin de crer et dinitialiser un objet de la
classe MPMusicPlayerController. Ici, on choisit applicationMusicPlayer, qui spcifie que le lecteur
sera propre votre application.Vous pourriez choisir iPodMusicPlayer, ce qui aurait pour effet dutiliser le lecteur iPod de votre iPhone. En quittant votre application, si elle joue de la musique, lapplication iPod prendra le relais et continuera la lecture.
Pour lancer la lecture on se sert dun bouton (playPauseButton). Afin dviter toute erreur, on le
dsactive ligne . On le ractive par la suite lorsque lon choisit un ou plusieurs morceaux.
Ligne , on lance une mthode qui permet de senregistrer des notifications la rception (en
terme de programmation KVO (Key Value Observing)). En fait, ligne , on spcifie que self sera un
observateur de lvnement le morceau en cours de lecture a chang. Chaque fois que ce sera le
cas, la mthode ligne sera appele.
Lorsque lon clique sur le bouton pour ajouter une chanson, un picker (MPMediaPickerController) apparat. Cest une vue que vous pouvez solliciter directement pour accder aux lments contenus dans liPod.
En une ligne, il saffiche lcran ! (ligne ). Cest le mme principe que pour accder lalbum photo.
Ligne , on choisit de ne prendre que la musique. Vous pouvez afficher les podcasts, les livres audio
ou tout le contenu multimdia de lappareil. Il est galement possible de slectionner plusieurs
morceaux la fois, comme on le spcifie ligne .
Se servir du picker implique dimplmenter deux mthodes delegate : une appele lorsque lutilisateur
valide son choix, et lautre lorsquil annule.Trs souvent, il faudra mettre au moins lappel ligne pour
enlever de la vue le picker. Pour que le buffer contenant la liste de lecture soit remis jour, il faut
appeler la mthode play ligne
. En fait, appeler play permet de sassurer que lon prend en compte
le nouveau buffer.

01_BlocN_iPhone.indd 108

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Passons aux mthodes appeles grce au systme de notifications. Nous allons implmenter plus tard
la mthode appele ligne lorsque le morceau change. Pour viter que le bouton Play ne soit actif
quand le lecteur na aucun morceau disponible la lecture, on ne lactive que lorsque le player nest
pas arrt (aucun morceau jou) ligne . Ligne , on change ltat du bouton pour afficher soit le
texte Play, soit Pause.
Continuons en affichant les donnes de la musique en cours et commenons par limage de lalbum.
Dans le viewDidLoad, ajoutez ces quelques lignes pour instancier une image view :

// Afchage des informations de la musique en cours


// limage qui afchera la couverture de lalbum
mediaArtworkImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 90,
200, 200)];
[self.view addSubview:mediaArtworkImageView];
Ensuite, dans la mthode gerer_LelementJoueEnCoursAChange:

// Lorsque le morceau jou change, on update lafchage de limage


- (void) gerer_LelementJoueEnCoursAChange: (id) notication {
MPMediaItem *currentItem = [musicPlayer nowPlayingItem];
UIImage *artworkImage = nil;
// On rcupre limage si elle existe
MPMediaItemArtwork *artwork = [currentItem valueForProperty:
MPMediaItemPropertyArtwork];
// On la transforme en image si elle existe
if (artwork) {
artworkImage = [artwork imageWithSize: CGSizeMake (200, 200)];
}
mediaArtworkImageView.image = artworkImage;
}
De plus, pour viter que limage ne reste quand la liste de lecture est finie dtre lue, ajoutez la ligne
en gras dans la mthode gerer_LetatPlayPauseAChange: :

if (playbackState == MPMusicPlaybackStateStopped) {
// On dsactive le bouton
[playPauseButton setEnabled:NO];
// on enlve limage
[mediaArtworkImageView setImage:nil];
// Mme si le lecteur tait arrt, on stoppe quand mme pour tre sr de
vider le buffer
[musicPlayer stop];
}
Ensuite, quelques informations concernant le morceau jou saffficheront dans un label, comme le
titre de la chanson, de lalbum, le nom de lartiste et le classement.

15

Jouer de la musique en secouant

01_BlocN_iPhone.indd 109

109

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

15

110

Jouer de la musique en secouant

Commencez par ajouter linitialisation du label dans le viewDidLoad :

// label pour afcher les informations de la chanson en cours


mediaPlayingInformationLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 300,
300, 100)];
mediaPlayingInformationLabel.numberOfLines = 0;
mediaPlayingInformationLabel.lineBreakMode = UILineBreakModeClip;
[self.view addSubview:mediaPlayingInformationLabel];
Ensuite, modifiez gerer_LelementJoueEnCoursAChange: :

// On afche les informations du morceau en cours


[mediaPlayingInformationLabel setText: [
NSString stringWithFormat: @%@ %@ %@ ; rate = %@,
[currentItem valueForProperty: MPMediaItemPropertyTitle], 
[currentItem valueForProperty: MPMediaItemPropertyArtist], 
[currentItem valueForProperty: MPMediaItemPropertyAlbumTitle], 
[currentItem valueForProperty: MPMediaItemPropertyRating]]]; 
if (musicPlayer.playbackState == MPMusicPlaybackStateStopped) {
[mediaPlayingInformationLabel setText:@Choisir une nouvelle liste !];
}
Ligne , on affiche le titre de la chanson, ligne , le nom de lartiste, ligne , le nom de lalbum
et ligne  le classement du morceau.
Pour finir, nous allons ajouter quelques contrles pour changer de morceau. Ajoutez donc deux
boutons dans le viewDidLoad :

// Gestion de la lecture de la musique


// bouton previous song
UIButton *previousSong = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[previousSong setFrame:CGRectMake(70, 50, 90, 30)];
[previousSong setTitle:@En arrire forState:UIControlStateNormal];
[previousSong addTarget:self action:@selector(previousSong:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:previousSong];
// bouton next song
UIButton *nextSong = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[nextSong setFrame:CGRectMake(170, 50, 90, 30)];
[nextSong setTitle:@En avant forState:UIControlStateNormal];
[nextSong addTarget:self action:@selector(nextSong:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:nextSong];

01_BlocN_iPhone.indd 110

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Comme vous le voyez, il faut ajouter ces deux mthodes :

- (void) nextSong:(id)sender {
[musicPlayer skipToNextItem];
}
- (void) previousSong:(id)sender {
[musicPlayer skipToPreviousItem];
}
Et noubliez pas de mettre jour le dealloc !

- (void)dealloc {
[musicPlayer stop];
[musicPlayer release];
[playPauseButton release];
NSNoticationCenter *noticationCenter = [NSNoticationCenter defaultCenter];
[noticationCenter removeObserver:self];
[mediaArtworkImageView release];
[mediaPlayingInformationLabel release];
[super dealloc];
}
Voil, vous avez un miniplayer qui devrait ressembler la Figure 15.1.

Figure 15.1 : Votre lecteur de musique.

15

Jouer de la musique en secouant

01_BlocN_iPhone.indd 111

111

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

15

112

Jouer de la musique en secouant

UTILISER LACCLROMTRE
Finissons cette fiche en nous intressant lacclromtre. Les donnes seront brutes, il faudra les
interprter en faisant un peu de mcanique des solides, puis mettre en forme avec les mathmatiques
du signal ! Nous allons voir comment dtecter le fait que lutilisateur secoue son iPhone.
Pour commencer, nous allons dfinir quelques constantes, juste en dessous du #import dans le
RootViewController.m
#dene kAccelerometerFrequency
40 // frquence (Hz) de mise jour des
donnes de lacclromtre
#dene kFilteringFactor
0.1 // constante utilise pour un ltre
passe haut
#dene kMinEraseInterval
0.5 // intervalle minimal en secondes
entre deux chantillonnages pour
dclencher la fonction secouer
#dene kEraseAccelerationThreshold
2.0 // seuil dacclration pour lequel
on dtecte la fonction secouer
Pour choisir la valeur de la frquence, rfrez-vous ces intervalles :

10-20 Hz. Adapt pour dterminer le vecteur dorientation de lappareil.


30-60 Hz. Adapt pour les jeux et autre applications qui ncessitent lacclromtre comme
donnes dentres (les jeux de voitures par exemple).

70-100 Hz. Adapt pour les applications qui dtectent des mouvements haute frquence comme
la dtection des mouvements saccads rapides.
Pas besoin de crer un objet de la classe UIAccelerometer pour lutiliser. crivez ces lignes dans le
viewDidLoad :
// on lance lchantillonnage de lacclromtre
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 /
kAccelerometerFrequency)]; 
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
Faisons une petite pause thorie : lorsque vous voyez une syntaxe comme la ligne , cela signifie
que la classe sollicite (ici UIAccelerometer) est une classe Singleton. En dautres termes, cest une
classe qui instancie elle-mme un objet de sa classe et le partage... Voyons a avec quelques lignes de
code.Voici quoi devrait ressembler la mthode sharedAccelerometer :
+ (UIAccelerometer *)sharedAccelerometer
{
static UIAccelerometer *sharedAccelerometer;
@synchronized(self) 
{
if (! sharedAccelerometer)
sharedAccelerometer = [[UIAccelerometer alloc] init];

return sharedAccelerometer; } return nil;


}
}

01_BlocN_iPhone.indd 112

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Oups ! quest-ce que @synchronized ligne  ? LObjective-C supporte le multithreading. Avec le


multithreading, deux threads sont susceptibles de modifier le mme objet au mme moment et cela
peut vite devenir un cauchemar... Pour protger des sections de code et ne leur permettre dtre
excutes que par un seul thread la fois, il existe le mot-cl @synchronized. Les autres threads sont
bloqus tant que lexcution de la section de code par le thread qui en a le droit nest pas sorti de la
section englobe par @synchronized.
Revenons notre acclromtre et implmentez la mthode delegate :
#pragma mark #pragma mark Accelerometer delegate method
- (void) accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
UIAccelerationValue
length,x,y,z; // ce sont les paramtres de notre
accelerometre

// On utilise un ltre passe-bas pour compenser linuence de la gravit :


on retrouve nos constantes dclares plus haut dans les #dene
myAccelerometer[0] = acceleration.x * kFilteringFactor +
myAccelerometer[0] * (1.0 - kFilteringFactor);
myAccelerometer  = acceleration.y * kFilteringFactor +
myAccelerometer  * (1.0 - kFilteringFactor);
myAccelerometer = acceleration.z * kFilteringFactor +
myAccelerometer * (1.0 - kFilteringFactor); 
// On calcule les valeurs pour nos 3 axes de lacclromtre (transformation
en ltre passe haut simpli)
x = acceleration.x - myAccelerometer[0];
y = acceleration.y - myAccelerometer;
z = acceleration.z - myAccelerometer;
// On calcule lintensit de laccleration courante
length = sqrt(x * x + y * y + z * z);
// Si liphone est secou, on fait quelque chose
// Pour utiliser lacclromtre, on scrute la diffrence de dplacement
ou/et dacclration entre deux instants donns
// La mthode CFAbsoluteTimeGetCurrent() permet dobtenir la date de
linstant t2, quon compare a linstant t1 stock dans lastTime
if((length >= kEraseAccelerationThreshold) && (CFAbsoluteTimeGetCurrent() >
lastTime + kMinEraseInterval)) 
{
[self performSelector:@selector(playOrPauseMusic:) withObject:nil];
// on met jour le dernier moment o lon a secou
lastTime = CFAbsoluteTimeGetCurrent();
}
}

15

Jouer de la musique en secouant

01_BlocN_iPhone.indd 113

113

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

15

114

Jouer de la musique en secouant

Ligne , on utilise un filtre passe bas simplifi, qui compensera linfluence de la gravit. Ici, on gnre
une valeur faite de 10 % (0,1 kFilteringFactor) de la valeur actuelle de lacclration et de 90 % de
la prcdente valeur.
Ligne , on soustrait la valeur obtenue avec le filtre passe bas la valeur de lacclration pour
obtenir un filtre passe haut simplifi. Le filtre passe haut nous permet disoler les mouvements
brusques. Enfin, on regarde si la force du shake est bien suprieure au seuil pour lequel on veut ragir,
et que nous navons pas reu un autre shake trop rcemment, ici 0,5 secondes (pour viter tout souci
avec plusieurs pics rapprochs dans lchantillonnage) ligne .
Notez que lon peut implmenter des filtres plus volus partir des quations obtenues en lectronique avec les filtres RC, RL...
Si vous compilez et secouez votre iPhone (avec au moins un morceau dans la liste bien sr), il se
mettra en Pause/Lecture !

LES FILTRES
Un filtre passe bas ne laisse passer que les donnes en basse frquence. Par exemple, quand vous
coutez de la musique, un filtre passe bas ne vous restituera que les basses sans les aigus.
Un filtre passe haut, cest linverse. Dans notre exemple du son, lapplication de ce filtre ne laisserait
passer que les aigus.

01_BlocN_iPhone.indd 114

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

16

Multitche et notifications locales


16 Multitche et notifications locales

Avec larrive de lOS 4.0, Apple a rpondu une forte demande des utilisateurs : laisser
une application tourner en tche de fond. Cependant, il y a certaines rgles respecter.
Enfin, nous utiliserons les notifications locales, qui ne ncessitent pas de serveur comme
pour le push !
Premier point important et non des moindres, le multitche de Apple nest pas du multitche classique. Et vous allez devoir travailler pour que votre application fonctionne en tche de fond. Zut ! me
direz-vous... Oui et non ! Certes, ce travail reprsente du temps, une comprhension du systme et
de la rflexion, mais il permet aussi doptimiser le comportement global du systme, ainsi que la
consommation dnergie.
En effet, Apple a choisi de garder la matrise des applications tournant en tche de fond. Au lieu
dautoriser un multitche pour toutes les applications lances, ce qui risquerait de diminuer de
manire drastique lautonomie de lappareil, votre application devra se conformer certaines rgles.
Pour tourner en tche de fond, votre application devra proposer au moins :

un service de localisation (ex. : avertir des radars sur la route proximit) ;


de la voip (Voice Over IP) (ex. : rester en ligne sur un rseau de voip) ;
la lecture de musique (ex. : lire de la musique en streaming).
De plus, vous pouvez lancer une tche lorsque lutilisateur appuie sur le bouton Home comme par
exemple finir un tlchargement ou raliser un upload. Par contre, ce temps est limit.

RENDRE

VOTRE APPLICATION MULTITCHE

Pour que votre application propose au moins un des trois services ci-dessus, voici quelques rgles
respecter :

IMPLMENTATION
Implmentez chacune de ces mthodes si possible afin de ragir chaque changements dtat :

application:didFinishLaunchingWithOptions: Informe que votre application vient dtre


lance. Cest ici que vous pourrez implmenter le chargement de fichiers xib par exemple.

applicationDidBecomeActive: Cette mthode est appele quand votre application passe du


statut inactif actif. Cela peut se produire par exemple lorsque lutilisateur ignore un appel ou un
SMS. Cest ici que vous pouvez remettre en route votre application si elle tait en pause par
exemple.

applicationWillResignActive: linverse, cette mthode est appele lorsque vous passez de


ltat actif inactif. Cest donc ici que vous pourrez mettre en pause en stoppant les timers de mise
jour en OpenGL par exemple.
applicationDidEnterBackground: Une nouvelle mthode apparue avec liOS 4.0. Elle sera appele
la place de applicationWillTerminate: lorsque lutilisateur appuie sur le bouton Home.

16

Multitche et notifications locales

01_BlocN_iPhone.indd 115

115

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

16

116

Multitche et notifications locales

Notez bien que vous devez utiliser cette mthode pour :

librer toute ressource inutile (par exemple laffichage dimages) ;


arrter les timers (invalidate) ;
sauver les donnes utilisateur ;
sauver assez dinformations pour tre en mesure de restaurer lapplication lorsquelle se relancera.

Info
Ce mcanisme de sauvegarde de la position dans lapplication est devenu trs courant. Pas de
panique, nous labordons la Fiche 35.

applicationWillEnterForeground: Cette mthode sera appele lorsque votre application passera


de ltat de tche de fond celui dactif.

applicationWillTerminate: Pour les appareils ne supportant pas le multitche, cette mthode est
classiquement appele quand lutilisateur appuie sur le bouton Home. Sinon, elle sera appele lorsque,
par exemple, le systme aura besoin de librer de la mmoire et quittera votre application.

A PPLICATION

EN TCHE DE FOND

Quand votre application est en tche de fond, suivez ces recommandations :

Ne continuez pas mettre jour votre interface graphique de quelle que manire que ce soit
(utilisation dOpenGL, mises jour dlments du UIKit...).

Naccdez pas aux ressources partages comme le carnet dadresses, les photos.
Annulez toute communication rseau (protocole Bonjour, stopper les connexions asynchrones).
Concentrez-vous sur une tche prcise car les ressources alloues sont limites. Ne faites donc
que ce qui est ncessaire !

Si vous avez besoin de temps (votre application a environ 5 secondes pour raliser ces tches), il
faudra recourir un autre mcanisme permettant de demander au systme du temps additionnel
pour raliser une tche. Nous le verrons plus tard dans cette fiche.

Tester imprativement si lappareil supporte le multitche et adapter le comportement en fonction :


UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
Dclarer dans le fichier Info.plist ce que votre application va faire parmi les trois services disponibles en ajoutant la cl UIBackgroundModes et en choisissant la bonne valeur.

CONSEILS
Applications ncessitant la localisation. Il est recommand de ne mettre jour frquemment
la position que si cela est vraiment ncessaire, comme les applications de guidage. Ainsi, il faut
recourir la mthode de CLLocationManager : - (void)startMonitoringSignicantLocationChanges.
Cette mthode ne gnrera un vnement que si lutilisateur bouge de manire significative.

01_BlocN_iPhone.indd 116

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

De plus, lavantage est que si votre application est arrte compltement, elle sera relance et
mise en tche de fond si la position de lutilisateur change.
Une autre faon de faire est de changer le .plist. Ainsi, votre application continuera dexploiter les
coordonnes GPS lorsque lutilisateur appuiera sur le bouton Home. Elle ne sera pas suspendue,
en attente, mais continuera dtre excute en tche de fond.

Applications utilisant la voip. Elles doivent configurer les sockets pour la voip et donc communiquer avec les serveurs distants (pour rester en ligne par exemple). Ensuite, elles doivent faire
appel la mthode setKeepAliveTimeout:handler: qui dterminera la frquence laquelle votre
application doit tre rveille pour maintenir le service. Le temps minimal tant de 600 secondes.
Ensuite, vous avez 30 secondes pour fournir une rponse, sinon, votre application quittera. Notez
galement que vous aurez trs certainement besoin de laudio, et donc de spcifier les deux
valeurs voip et audio dans le .plist.

LES BLOCKS
Si vous souhaitez raliser une tche au moment o votre application sera suspendue, comme lupload
dun fichier, il faut tout dabord apprendre une nouvelle syntaxe un peu particulire : les blocks. Bien
que crs par Apple, ils ressemblent fortement aux fonctions en C. En fait, les blocks sont intgrs
dans les fonctions et peuvent ainsi exploiter directement les variables locales. On les utilise galement
en tant que callbacks.
Voici un exemple de block :

int adder = 5;
int (^addTheAdder)(int) = ^(int toAdd) {
return toAdd + adder;
};
NSLog(@%d, addTheAdder(45));
// retournera 50 !
Ici, on dclare donc une variable addTheAdder qui retourne un int et prend un int en paramtre. Notez
que lon accde la variable locale adder dans le block. On peut copier un block, le passer en paramtre dune mthode, le passer un autre thread...
De plus, il faudra utiliser GCD (Grand Central Dispatch) qui est un mcanisme simplifiant lappel
diffrents threads pour raliser des tches. En clair, plutt que de laisser le dveloppeur grer manuellement les threads (allocation, gestion des ressources entre threads, etc.) et risquer de ne rien optimiser, Apple a cr GCD qui est un code open source. On peut utiliser GCD en bas niveau, avec du
C, comme cela va tre le cas dans le code ci-aprs, ou en plus haut niveau si vous tes plus frileux,
avec des classes comme NSOperationQueue, NSInvocationOperation. Pour simplifier, vous allez dire
GCD : Tiens, voil un block de code excuter quand tu peux, dbrouille-toi ! (sous-entendu bien sr :
Cre un ou plusieurs threads pour le faire, mais je ne veux pas le savoir.).

16

Multitche et notifications locales

01_BlocN_iPhone.indd 117

117

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

16

118

Multitche et notifications locales

Voici maintenant la mthode implmenter pour raliser une tche dans un temps additionnel (dans
lapplication delegate) :

- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication* app = [UIApplication sharedApplication];
// Demande la permission de fonctionner en tche de fond.
// Dans le cas o la tche dure trop longtemps, on cre un gestionnaire
dexpiration
NSAssert(self->bgTask == UIInvalidBackgroundTask, nil);
self->bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
// Dans le cas o la tche nit quasiment en mme temps
// que lappel sur le thread principal, on synchronise
dispatch_async(dispatch_get_main_queue(), ^{
if (self->bgTask != UIInvalidBackgroundTask)
{
[app endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
}
});
}];
// On commence la tche et on la retourne immdiatement
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// Faire ici le travail associ la tche


// on synchronise lappel sur le thread principal dans le cas
//o le gestionnaire dexpiration se dclenche en mme temps
dispatch_async(dispatch_get_main_queue(), ^{
if (self->bgTask != UIInvalidBackgroundTask)
{
[app endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
}
});
});
}
Ne pas oublier de dclarer dans le .h : UIBackgroundTaskIdentier bgTask;.

01_BlocN_iPhone.indd 118

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

UTILISER

LES NOTIFICATIONS LOCALES

Passons maintenant aux notifications locales et crons un nouveau projet Window-based Application
nomm Reveil et ajoutez-y un RootViewController. Modifiez-le comme suit :

RootViewController.h
#import <UIKit/UIKit.h>
#dene kRepeatAlarm @repeatAlarm 
#dene kDate @date
@interface RootViewController : UIViewController {
BOOL repeatAlarm;
NSDate *scheduleDate;
}
- (void)scheduleAlarmForDate:(NSDate*)theDate;
@end
RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void)viewDidLoad {
// Construction des lments dinterface
UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0,
245, 0, 0)]; 
datePicker.datePickerMode = UIDatePickerModeTime;
[datePicker addTarget:self action:@selector(hasChangeDate:)
forControlEvents:UIControlEventValueChanged];
[self.view addSubview:datePicker];
UILabel *labelRepeat = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 170, 30)];
labelRepeat.text = @Tous les jours ?;
[self.view addSubview:labelRepeat];
[labelRepeat release];
UISwitch *switchRepeat = [[UISwitch alloc] initWithFrame:CGRectMake(200, 30,
50, 30)];

[switchRepeat addTarget:self action:@selector(switchHandle:)


forControlEvents:UIControlEventValueChanged];
[self.view addSubview:switchRepeat];
[switchRepeat release];

16

Multitche et notifications locales

01_BlocN_iPhone.indd 119

119

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

16

120

Multitche et notifications locales

UIButton *buttonScheduleAlarm = [UIButton buttonWithType:UIButtonTypeRoundedRect];


[buttonScheduleAlarm setTitle:@Ajouter lalarme forState:UIControlStateNormal];
[buttonScheduleAlarm setFrame:CGRectMake(10, 60, 300, 30)];
[buttonScheduleAlarm addTarget:self action:@selector(scheduleAlarm:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonScheduleAlarm];
// gestion de la sauvegarde 
repeatAlarm = [[NSUserDefaults standardUserDefaults]
boolForKey:kRepeatAlarm];
NSString *dateSavedString =
[[NSUserDefaults standardUserDefaults] stringForKey:kDate];

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];


dateFormatter.dateFormat = @dd MM yyyy HH:mm;
NSDate* date = [dateFormatter dateFromString:dateSavedString];
if (date) {
[datePicker setDate:date animated:YES];
}
else {
[datePicker setDate:[NSDate date] animated:YES];
}
[datePicker release];
[super viewDidLoad];
}
- (void) hasChangeDate:(id)sender {
UIDatePicker *aDatePicker = (UIDatePicker*)sender;
scheduleDate = [aDatePicker date];
}
- (void) scheduleAlarm:(id)sender {
NSCalendar *gregorian = [[[NSCalendar alloc]
initWithCalendarIdentier:NSGregorianCalendar] autorelease];
NSDateComponents *aComponent = [gregorian components:(NSDayCalendarUnit |
NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit |
NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:scheduleDate];
[aComponent setSecond:0]; 
NSDate *theDate = [gregorian dateFromComponents:aComponent];
[self scheduleAlarmForDate:theDate];
// sauvegarde de la date du rveil

01_BlocN_iPhone.indd 120

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];


dateFormatter.dateFormat = @dd MM yyyy HH:mm;
NSString* dateString = [dateFormatter stringFromDate:theDate];
[[NSUserDefaults standardUserDefaults] setObject:dateString forKey:kDate];
}
(void) switchHandle:(id)sender {
UISwitch *aSwitch = (UISwitch*)sender;
repeatAlarm = aSwitch.on;
}
- (void)scheduleAlarmForDate:(NSDate*)theDate
{
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotications = [app scheduledLocalNotications];
// On enlve toutes les anciennes notications avant den recrer une
if ([oldNotications count] > 0)
[app cancelAllLocalNotications]; 
// On cre une nouvelle notication
UILocalNotication* alarm = [[[UILocalNotication alloc] init] autorelease];
if (alarm)
{
alarm.reDate = theDate;
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = repeatAlarm ? kCFCalendarUnitDay : 0; 
alarm.soundName = UILocalNoticationDefaultSoundName;
alarm.alertBody = @Cest lheure !;
alarm.alertAction = @Se rveiller !;
[app scheduleLocalNotication:alarm]; 
}
}
- (void)dealloc {
[super dealloc];
}
@end
Ligne , on alloue un UIDatePicker qui permet de choisir lheure laquelle la notification se
dclenche. Notez que lon met 0 en largeur et hauteur, car le UIDatePicker va grer lui-mme ses
dimensions. De plus, on choisit ici de nafficher que lheure et les minutes.

16

Multitche et notifications locales

01_BlocN_iPhone.indd 121

121

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

16

122

Multitche et notifications locales

Ensuite, ligne , on rcupre la date depuis un fichier de prfrences. La classe NSDate respectant le
protocole NSCoding, nous pourrions lutiliser pour la sauver dans un fichier. Ici, dans le but purement
pdagogique de faire manipuler les dates et strings, on choisit de la sauver sous forme de chane de
caractres. Les cls ncessaires sont dfinies ligne . Pour que la notification soit dclenche au
changement de la minute, on met 0 pour les secondes ligne . Ainsi, si la date tait
10 10 2010 13h34m08s, elle deviendrait 10 10 2010 13h34m00s.
On peut prvoir plusieurs notifications en mme temps, mais nous choisissons ici dannuler toutes les
prcdentes notifications mises en place par lapplication ligne . Les notifications peuvent tre
rptes ligne . 0 correspondant aucune rptition. Ici, on choisit de tester repeatAlarm. Sil est
YES, alors on rpte tous les jours, sinon on ne rpte pas. Avec la proprit soundName, on peut
dfinir un son pour lalarme partir dun fichier dans le main bundle de type caf, wav ou aiff. Par dfaut
il ny en a aucun, et on peut galement mettre le son par dfaut avec UILocalNoticationDefaultSoundName. La proprit alertBody quant elle, dfinit le texte de lalerte de la notification. Le titre
de lalerte tant le nom de lapplication, voir Figure 16.1. Lorsque lcran de votre iPhone est verrouill,
vous pouvez galement choisir le texte affich la place de dverrouiller via la proprit alertAction
(voir Figure 16.2).
Enfin, ligne , on programme la notification !

Figure 16.1 : Une notification


reue sur l cran des applications.

01_BlocN_iPhone.indd 122

Figure 16.2 : Une notification reue


sur l cran de veille.

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

Communiquer par Bluetooth


17 Communiquer par Bluetooth

LiPhone intgre une puce bluetooth et ce titre, vous pouvez lutiliser pour crer des
applications communicantes entre appareils proximit.

Attention
Nous avons besoin ici du framework GameKit. Pour tester votre application, vous devrez possder
deux appareils. Cependant si vous nen avez quun, rien ne vous empche de suivre cette fiche pour
comprendre le mcanisme !
Mais quel est lobjectif de cette fiche ?
Dans le SDK iOS est intgr le GameKit qui vous permet entre autres de grer des connexions
bluetooth entre appareils. Nous allons ici crer un petit jeu de type pierre feuille ciseau entre deux
appareils. Pour cela, il faudra dans un premier temps grer les connexions entre appareils, puis mettre
en place le mcanisme de dialogue (protocole) pour les faire communiquer correctement.
Raliser une connexion par Bluetooth entre deux appareils est relativement facile. Le plus dur consiste
grer le format de donnes qui transitent.

CRER

UN NOUVEAU PROJET ET INITIALISER

LA COMMUNICATION
Pour commencer, crez un nouveau projet de type View-based Application que vous nommerez PierreFeuilleCiseau. Ensuite, ajoutez le framework GameKit en cliquant du bouton droit sur le dossier,
dans votre projet, intitul Frameworks puis add > Existing Frameworks et choisir GameKit.
Dans le fichier PierreFeuilleCiseauViewController.h, faites limport du framework :

#import <GameKit/GameKit.h>
La premire tape consiste crer un bouton de connexion/dconnexion pour crer le rseau bluetooth. Pour cela, dclarer le bouton dans le .h :

Info
Par la suite, nous ne travaillerons que dans la classe PierreFeuilleCiseauViewController. .h
correspondra donc PierreFeuilleCiseauViewController.h et .m PierreFeuilleCiseauViewController.m.

// bouton pour la connexion / dconnexion


UIButton *buttonConnectDisconnect;
Pour crer ce rseau, il faudra utiliser un objet de la classe GKPeerPickerController pour afficher
lalerte prsente dans le SDK permettant de voir les appareils proximit. Ensuite, lorsque lutilisateur
aura choisi un appareil, il faudra crer une session. Pour cela, dclarez dans le .h :

// dclaration du picker pour se connecter au BT


GKPeerPickerController *btPicker;
// dclaration de la session BT

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 123

123

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

124

Communiquer par Bluetooth

GKSession *currentSession;
Dans le .m, commenons par crer le bouton qui sera unique pour la connexion/dconnexion. Dans
le viewDidLoad, ajoutez :

// Bouton de connexion / dconnexion


buttonConnectDisconnect = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[buttonConnectDisconnect addTarget:self action:@selector(connectToBluetooth:)
forControlEvents:UIControlEventTouchUpInside];
[buttonConnectDisconnect setTitle:@Connexion forState:UIControlStateNormal];
[buttonConnectDisconnect setTitle:@Dconnexion forState:UIControlStateSelected];
[buttonConnectDisconnect setFrame:CGRectMake(30, 30, 260, 30)];
[self.view addSubview:buttonConnectDisconnect];
Comme vous le voyez, il faut implmenter la mthode connectToBluetooth: qui servira se
connecter/dconnecter du rseau :

#pragma mark #pragma mark Bluetooth Manager


-(void) connectToBluetooth :(id) sender {
UIButton *buttonSender = (UIButton*)sender;
// on pourrait aussi rcuprer partir de buttonConnectDisconnect
BOOL wantToDisconnect = buttonSender.selected;
if (wantToDisconnect)
{
// on se dconnecte
[self.currentSession disconnectFromAllPeers];
// on libre la session
[currentSession release];
}
else
{
// dclaration du picker
btPicker = [[GKPeerPickerController alloc] init];
// self est delegate
btPicker.delegate = self; 
// GKPeerPickerConnectionTypeOnline : une connexion depuis Internet
// GKPeerPickerConnectionTypeNearby : une connexion locale depuis le bluetooth
btPicker.connectionTypesMask = GKPeerPickerConnectionTypeNearby; 
// on afche le picker
[btPicker show];
}
// on change ltat du bouton
buttonSender.selected = !buttonSender.selected;

01_BlocN_iPhone.indd 124

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
En ligne , vous remarquez que vous pouvez choisir entre une connexion locale en utilisant le
Bluetooth (GKPeerPickerConnectionTypeNearby) ou une connexion Internet (GKPeerPickerConnectionTypeOnline). Nous naborderons pas ce dernier point ici.
Lorsque le picker saffichera lcran, il faudra rpondre certaines mthodes delegate ligne .
Ajoutez le protocole en gras dans le .h :

@interface PierreFeuilleCiseauViewController : UIViewController


<GKPeerPickerControllerDelegate>
Puis, regardons la mthode appele lorsque lutilisateur a choisi un ami proximit et a accept la
communication. Il faudra notamment initialiser la session. Ajoutez ces lignes dans le .m :
#pragma mark #pragma mark Bluetooth delegate
// la connexion est tablie
- (void)peerPickerController:(GKPeerPickerController *)picker
didConnectPeer:(NSString *)peerID toSession:(GKSession *) session {
// on rcupre le peerId de la personne connecte
self.gamePeerId = peerID; // copy 
// on rcupre la session
self.currentSession = session;
// self est dlgu de la session
session.delegate = self; 
// on met self pour recevoir les donnes entrantes
[session setDataReceiveHandler:self withContext:NULL]; 
// on enlve le delegate du picker
picker.delegate = nil;
// on enlve le picker et on le release
[picker dismiss];
[picker autorelease];
}
Ligne , nous rcuprons lidentifiant de lutilisateur distant qui vient daccepter de rejoindre le
rseau. Il faut tout dabord le dclarer dans le .h :

NSString *gamePeerId;
Puis utiliser les getters/setters pour gamePeerId et currentSession. Dans le .h :

@property (nonatomic, copy) NSString *gamePeerId;


@property (nonatomic, retain) GKSession *currentSession;
Dans le .m :
@synthesize currentSession, gamePeerId;

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 125

125

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

126

Communiquer par Bluetooth

Info
Pour rcuprer le nom du peer id, il suffit de faire :

NSLog(@peer id nom : %@, [currentSession displayNameForPeer:peerID]);


Sauvegarder cet identifiant est primordial car il sera ncessaire pour communiquer par la suite !
La classe PierreFeuilleCiseauViewController est dlgue de la session ligne . ce titre, il faut
modifier le .h :

@interface PierreFeuilleCiseauViewController : UIViewController


<GKPeerPickerControllerDelegate, GKSessionDelegate>
Dans le .m, il faut implmenter les mthodes dlgues qui correspondent. La premire consiste
grer les changements dtats de la connexion :
#pragma mark #pragma mark Delegate de la session
//appele lorsque la session change dtat
- (void)session:(GKSession *)session peer:(NSString *)peerID
didChangeState:(GKPeerConnectionState)state {
switch (state)
{
case GKPeerStateConnected:
NSLog(@Connect);
break;
case GKPeerStateDisconnected:
NSLog(@Dconnect);
// on release la session en cours
[currentSession release];
// on remet le bouton dans ltat de connexion
buttonConnectDisconnect.selected = NO;
break;
default:
break;
}
}
La deuxime consiste nommer la session pour sassurer que les appareils qui lancent votre application ne communiquent QUE entre eux et ne peuvent accepter dautres appareils ne possdant pas
lapplication :

- (GKSession *)peerPickerController:(GKPeerPickerController *)picker


sessionForConnectionType:(GKPeerPickerConnectionType)type {
GKSession *session = [[GKSession alloc] initWithSessionID:kRockPaperScissorSessionID
displayName:nil sessionMode:GKSessionModePeer];
return [session autorelease];
}

01_BlocN_iPhone.indd 126

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Il faut dfinir lidentifiant dans le .m :

#dene kRockPaperScissorSessionID @rockPaperScissor // lid de la session


Enfin, ligne , il faut spcifier une classe qui va rceptionner les donnes provenant des utilisateurs.
Ici, cest la classe PierreFeuilleCiseauViewController qui sen charge.
Dans le cas o lutilisateur annulerait la connexion, ajoutez :

// la connexion a t annule
- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker
{
// on release le picker
picker.delegate = nil;
[picker autorelease];
// on revient au bouton pour la connexion
buttonConnectDisconnect.selected = NO;
}
a y est, nous venons de connecter deux appareils ! Mais rien nest gagn encore...

TRANSMETTRE

DES DONNES

Tout dabord, revenons aux fondamentaux avant de se compliquer la tche. Pour transmettre des
donnes, il faut envoyer un objet de type NSData. Ainsi, si vous souhaitez par exemple envoyer un
objet de la classe NSString, il suffira de faire :

NSString *string = @Toto ou votre texte ici !;


NSData *data = [string dataUsingEncoding: NSASCIIStringEncoding];
[currentSession sendDataToAllPeers:data withDataMode:GKSendDataReliable error:nil];
En fait, il suffit simplement de lencoder et de le transmettre sous forme de donnes (octets). Vous
pouvez choisir le mode de transmission, ici GKSendDataReliable. Cela signifie que la donne sera
envoye en boucle tant quelle ne sera pas reue, en fonction dun time out bien sr. Le time out est
le temps maximal pendant lequel le systme va essayer de transmettre les donnes. Pass ce temps,
le systme abandonnera pour rendre la main. Utilisez ce mode de transmission lorsque les donnes
transmettre ncessitent dtre reues. Si, par exemple, vous envoyez frquemment des donnes de
mise jour de la position de lutilisateur, servez-vous de GKSendDataUnreliable car ce nest pas trs
important si un rafrachissement manque lappel.
Pour recevoir le string et lafficher sur les autres appareils, il faut crire :

- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer


inSession:(GKSession *)session context:(void *)context {
NSString *string = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
NSLog(@*@, string);
[string release];
}

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 127

127

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

128

Communiquer par Bluetooth

Info
Il est recommand de ne pas changer de trop gros paquets de donnes la fois, pour viter de
surcharger la connexion. Il faudra donc penser dcouper les donnes envoyer en paquets si besoin.
La taille recommande est de 1 000 octets soit peu prs 1 Ko (1 Ko = 1024 octets). Ne loubliez pas !
Nous allons un peu compliquer laffaire ! Pour cela, il faut revenir au papier et au crayon puis dessiner.
En fait, pour faciliter la comprhension et surtout lchange de donnes, il est prfrable de dfinir un
serveur et un client. Ainsi, lorsque vous construisez larchitecture du programme et des changes puis
crivez le code correspondant, raisonner en un mme endroit pour la fois le serveur et le client
peut vite devenir un cauchemar. Il faut rester trs concentr. Pour viter tout souci, je vous conseille
trs fortement de concevoir une machine tat. Je vous propose une architecture (qui nest pas
unique mais que je suivrai par la suite) la Figure 17.1 (ct serveur) et 17.2 (ct client).
Ct serveur
Dbut
Envoi du coin toss
init

/start

Start

Envoi du
dcompte

Dcompte

dcompte 0

Figure 17.1 : Machine


d tat ct serveur.

dcompte = 0
Attente
action client

/actionClient

actionClient
Calcul du
rsultat

Envoi du
rsultat au client

Affichage
du rsultat

01_BlocN_iPhone.indd 128

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ct Client
Dbut

Envoi du coin toss

init

/startDecompte

startDecompte
Dcompte

dcompte 0

dcompte = 0

Figure 17.2 : Machine


d tat ct client.

Envoi action
choisie

Attente du
rsultat

/rsultat

rsultat
Affichage du
rsultat

Pour commencer, on va se proccuper de linterface graphique. Il faudra ajouter trois boutons pour
chaque action (Pierre, Feuille, Ciseau), un bouton pour lancer le jeu ct serveur, un label pour afficher
si lon est serveur ou client et un label qui affichera le dcompte.
Ajoutez ces lignes dans le viewDidLoad :

// bouton pour commencer le jeu


buttonStartGame = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[buttonStartGame addTarget:self action:@selector(startGame:)
forControlEvents:UIControlEventTouchUpInside];
[buttonStartGame setTitle:@Commencer forState:UIControlStateNormal];
[buttonStartGame setFrame:CGRectMake(60, 70, 200, 30)];
// initialement on cache le bouton
buttonStartGame.hidden = YES;
[self.view addSubview:buttonStartGame];
// Label pour spcier le statut (serveur ou client)
labelServerOrClient = [[UILabel alloc] initWithFrame:CGRectMake(60, 110, 200, 30)];
labelServerOrClient.textAlignment = UITextAlignmentCenter;
labelServerOrClient.backgroundColor = [UIColor clearColor];
labelServerOrClient.text = @Connectez-vous;
[self.view addSubview:labelServerOrClient];

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 129

129

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

130

Communiquer par Bluetooth

// Bouton feuille
UIButton *buttonPaper = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonPaper addTarget:self action:@selector(gameAction:)
forControlEvents:UIControlEventTouchUpInside];
[buttonPaper setFrame:CGRectMake(60, 190, 200, 30)];
[buttonPaper setTitle:@Feuille forState:UIControlStateNormal];
// on utilise le tag pour savoir quoi correspond le bouton
[buttonPaper setTag:kPaper];
[self.view addSubview:buttonPaper];
// Bouton pierre
UIButton *buttonRock = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonRock addTarget:self action:@selector(gameAction:)
forControlEvents:UIControlEventTouchUpInside];
[buttonRock setFrame:CGRectMake(60, 230, 200, 30)];
[buttonRock setTitle:@Pierre forState:UIControlStateNormal];
// on utilise le tag pour savoir quoi correspond le bouton
[buttonRock setTag:kRock];
[self.view addSubview:buttonRock];
// Bouton Ciseau
UIButton *buttonScissor = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonScissor addTarget:self action:@selector(gameAction:)
forControlEvents:UIControlEventTouchUpInside];
[buttonScissor setFrame:CGRectMake(60, 270, 200, 30)];
[buttonScissor setTitle:@Ciseau forState:UIControlStateNormal];
// on utilise le tag pour savoir quoi correspond le bouton
[buttonScissor setTag:kScissor];
[self.view addSubview:buttonScissor];

// label dcompte
labelCountDown = [[UILabel alloc] initWithFrame:CGRectMake(60, 420, 200, 30)];
labelCountDown.textAlignment = UITextAlignmentCenter;
labelCountDown.backgroundColor = [UIColor clearColor];
labelCountDown.text = @;
[self.view addSubview:labelCountDown];
Cest un peu long, mais il ny a rien de compliqu si vous avez suivi correctement les fiches prcdentes.Vous devrez ajouter dans le .h :

// Label pour notier si lutilisateur est serveur ou client


UILabel *labelServerOrClient;
// label dcompte
UILabel *labelCountDown;

01_BlocN_iPhone.indd 130

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Soyez attentifs cependant car nous employons la proprit tag pour identifier les objets. En fait, nous
pourrions trs bien crer une mthode par action, mais cela peut vite encombrer votre code. Vous
laurez compris, il faut dfinir kScissor, kRock et kPaper. Pour cela, ajoutez dans le .m (en dehors de
limplmentation) ce type numr :

// dnition des tats possibles du jeu !


typedef enum {
kRock,
kPaper,
kScissor
} gameAction;
Ensuite, comme nous sommes dans les types numrs, dfinissons les statuts possibles (toujours au
mme endroit) :

// dnition de qui est serveur et qui est client


typedef enum {
kServer,
kClient
} gameNetwork;
Enfin, on dfinit les diffrentes tapes dont on va se servir dans la mise en place du protocole (voir
les machines dtats aux Figures 17.1 et 17.2).

// les diffrents tats ncessaires


typedef enum {
NETWORK_COINTOSS,
// Pour dcider qui va tre le serveur
NETWORK_SEND_START_SIGNAL, // On envoie ce paquet pour commencer le compte rebours
NETWORK_SEND_ACTION, // On envoie pierre, feuille ou ciseau
NETWORK_RESULT // on envoie le rsultat du jeu
} packetCodes;
Ensuite, implmentons la mthode pour envoyer des donnes. En fait, plutt que denvoyer un
morceau de texte comme nous lavons vu juste avant, nous allons construire une trame qui contiendra
un en-tte et les donnes. Dans len-tte, vous pouvez dfinir ce que vous souhaitez. Ici, nous ne nous
proccuperons que dun identifiant parmi la structure numre packetCodes. Cela permet au destinataire dtre en mesure dinterprter les donnes quil reoit et pouvoir raliser laction dfinie par
le protocole. Notez quil est galement de bon ton dinclure dans len-tte un comptage du nombre
de trames. Ainsi, le destinataire peut les rassembler dans lordre. Ici, nous nchangeons pas de
donnes compliques, ce nest donc pas ncessaire.

#pragma mark #pragma mark Envoi rception des paquets


- (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID
withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend {
// On va construire une trame pour le paquet
static unsigned char networkPacket[kMaxPacketSize]; 

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 131

131

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

132

Communiquer par Bluetooth

// on a un entier pour lentte : il contient lid du packet (dni dans


packetCodes)

const unsigned int packetHeaderSize = sizeof(int); 


// on vrie bien que la taille du paquet est la bonne, sinon on ignore
if(length < (kMaxPacketSize - packetHeaderSize)) {
int *pIntData = (int *)&networkPacket[0];
// on remplit le header
pIntData[0] = packetID; 
// on copie la data la suite
memcpy( &networkPacket[packetHeaderSize], data, length ); 
// on convertit le paquet en objet NSData, ncessaire pour envoyer les
donnes
NSData *packet = [NSData dataWithBytes: networkPacket length:
(length+packetHeaderSize)];
if(howtosend == YES) {
[session sendData:packet toPeers:[NSArray arrayWithObject:gamePeerId]
withDataMode:GKSendDataReliable error:nil]; 
} else {
[session sendData:packet toPeers:[NSArray arrayWithObject:gamePeerId]
withDataMode:GKSendDataUnreliable error:nil];
}
}
}
En , on rserve un tableau de la taille maximale quil faut dfinir en crivant :

#dene kMaxPacketSize 1024 // on rserve un peu plus. Cela correspond la


limite conseille pour la taille dun paquet
En , on dfinit la taille de len-tte. Ici, nous ne rservons de la place que pour un entier sur 32 bits
(4 octets). sizeOf(int) renvoie donc 4.
En , on remplit len-tte puis en , on copie les donnes la suite de len-tte.Vous noterez quen
, nous nenvoyons pas les donnes tous les utilisateurs susceptibles dtre connects, mais une
liste que nous avons choisie. Ici, seuls deux utilisateurs peuvent communiquer en mme temps.
Lenvoi tant dfini, il faut galement raliser la partie rception. Pour linstant, ce sera une bote vide :
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer
inSession:(GKSession *)session context:(void *)context {
unsigned char *incomingPacket = (unsigned char *)[data bytes];
int *pIntData = (int *)&incomingPacket[0];
int packetID = pIntData[0];
switch( packetID ) {
case NETWORK_COINTOSS:
{

01_BlocN_iPhone.indd 132

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
break;
case NETWORK_SEND_START_SIGNAL:
{
}
break;
case NETWORK_SEND_ACTION:
{
}
break;
case NETWORK_RESULT:
{
}
break;
default:
// erreur
break;
}
}
Commenons par le ct serveur (Figure 17.1). La premire tape est de demander un identifiant
lappareil en face pour savoir qui sera serveur...
Pour scuriser lidentifiant et sassurer quil soit unique, nous exploitons lUDID (Unique Device ID) du
tlphone que nous venons ensuite hasher (crer une valeur unique de telle manire quil soit impossible de retrouver loriginal). Pour cela, crire dans le viewDidLoad :

// Scurit de lchange
// on rcupre lUDID de lappareil
NSString *udid = [[UIDevice currentDevice] uniqueIdentier];
// on le hash pour protger la donne
gameUniqueID = [udid hash];
Il faut dfinir gameUniqueID dans le .h :

int gameUniqueID;
Les deux appareils vont envoyer la requte avec comme identifiant NETWORK_COINTOSS. Pour cela, nous
allons la raliser immdiatement aprs la connexion. Ajoutez cette ligne dans peerPickerController
:didConnectPeer:toSession: :

// on envoie le cointoss pour savoir qui sera client / serveur


[self sendNetworkPacket:currentSession packetID:NETWORK_COINTOSS
withData:&gameUniqueID ofLength:sizeof(int) reliable:YES];
Lors de la rception, ce sera lappareil qui aura le plus grand id qui sera serveur, lautre sera client. Il
faut trancher par moments ! Ajoutez les lignes en gras :

case NETWORK_COINTOSS:
{

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 133

133

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

134

Communiquer par Bluetooth

// on rcupre le coin toss pour savoir qui est serveur client


int coinToss = pIntData ;
// si le jeton de lautre est plus grand que le notre, il est serveur...
if(coinToss > gameUniqueID) {
peerStatus = kClient;
}
else {
peerStatus = kServer;
}
// on cache si on est client le bouton pour lancer le dcompte
buttonStartGame.hidden = peerStatus == kClient;
// on notie lutilisateur si il est serveur ou client
labelServerOrClient.text = (peerStatus == kServer) ? @Serveur : @Client;
}
Dclarez dans le .h la variable peerStatus :

NSInteger peerStatus;
On linitialisera client dans le viewDidLoad :

// on se met par dfaut en client


peerStatus = kClient;
Aprs avoir dfini qui tait le serveur/client, le serveur attend que lutilisateur clique sur le bouton
Commencer pour lancer le dcompte et informer le client de dbuter galement le dcompte.
Pour cela, on implmente la mthode appele quand on clique sur le bouton Commencer. Le bouton
ne sera visible que par le serveur, selon ce qui a t dfini ci-dessus.

- (void) startGame:(id)sender {
NSLog(@Start game);
currentCountDown = kStartCounter; 
if (peerStatus == kServer) {
// si on est serveur, on envoie le signal de dpart et on commence le
dcompte
[self sendNetworkPacket:currentSession packetID:NETWORK_SEND_START_SIGNAL
withData:[NSDate date] ofLength:sizeof(NSDate) reliable:YES]; 
[self decrementCount:nil]; 
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:@selector(decrementCount:) userInfo:nil repeats:YES]; 
}
// sinon, on ne fait rien et on attend le signal de dpart dans la mthode
de rception des donnes
}
Avant toute chose, pour que la ligne  soit correcte, il faut dclarer dans le .h :

01_BlocN_iPhone.indd 134

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

int currentCountDown;
et dfinir le nombre o lon commence dcompter dans le .m :

#dene kStartCounter 5 // on a 5 secondes pour choisir


Puis, ligne , on envoie une trame avec lidentifiant correspondant au lancement du dcompte. On
choisit de passer la date de lenvoi si lon souhaitait faire un petit calcul par la suite pour rattraper le
retard d la latence de la communication.
Aprs cette ligne, il faut commencer dcompter. Le timer est appropri pour raliser ceci, mais il
faut appeler une premire fois la mthode la main (ligne ) pour commencer directement
dcompter. Le timer est ensuite lanc ligne .
Le compte rebours dclench, il faut que lutilisateur clique rapidement sur Papier, Feuille ou Ciseau.
Dans la mthode appele par ces boutons, nous allons mettre de ct la valeur clique. Pour cela, crire :

- (void) gameAction:(id)sender {
UIButton *buttonSender = (UIButton*)sender;
// on rcupre laction depuis le tag du bouton
myGameAction = buttonSender.tag;
}
Oui, myGameAction doit tre dfinie dans le .h :

int myGameAction;
On linitialise galement dans le viewDidLoad :
// on met par dfaut feuille comme game action
myGameAction = kPaper;
Ct serveur, une fois que le dcompte est lanc, il attend de recevoir laction du client. Une fois
quelle est reue, il va chercher qui est le gagnant. Pour cela, ajoutez les lignes en gras :
case NETWORK_SEND_ACTION:
{
// on vient de recevoir laction du client
int clientAction = pIntData ;
[self performResultWithClientAction:clientAction];
}
break;
Ensuite, on dfinit qui est gagnant du client ou du serveur :

- (void) performResultWithClientAction:(int)clientAction {
int clientResult;
switch (myGameAction) {
case kPaper:
switch (clientAction) {
case kPaper:
clientResult = kEquality;
break;
case kRock:

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 135

135

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

Communiquer par Bluetooth

136

clientResult = kLose;
break;
case kScissor:
clientResult = kWin;
break;
default:
break;
}
break;
case kRock:
switch (clientAction) {
case kPaper:
clientResult = kWin;
break;
case kRock:
clientResult = kEquality;
break;
case kScissor:
clientResult = kLose;
break;
default:
break;
}
break;
case kScissor:
switch (clientAction) {
case kPaper:
clientResult = kLose;
break;
case kRock:
clientResult = kWin;
break;
case kScissor:
clientResult = kEquality;
break;
default:
break;
}
break;
default:
break;
}
}

01_BlocN_iPhone.indd 136

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Il y a comme vous le savez trois tats possibles : perdre, gagner, ou tre galit. Ajoutez le type
numr dans le .m :

// cas possibles pour gagner, perdre, ou galit


typedef enum {
kWin,
kLose,
kEquality
} resultStatus;
Ct serveur, la dernire action est de notifier le client du rsultat et de lafficher. Pour ce faire,
crivez la fin de performResultWithClientAction: :

// si on est serveur, on avertit le client de sa dfaite ou non


if (peerStatus == kServer) {
[self sendNetworkPacket:currentSession packetID:NETWORK_RESULT
withData:&clientResult ofLength:sizeof(int) reliable:YES];
// on afche le rsultat
[self displayResultForClientResult:clientResult];
}
Laffichage du rsultat se fera dans une alert view. Il faudra tout dabord faire correspondre le message
avec le rsultat. Pour cela, crivez :

- (void) displayResultForClientResult:(int) clientResult {


NSString *resultString;
if (peerStatus == kClient) {
switch (clientResult) {
case kWin:
resultString = @gagn;
break;
case kLose:
resultString = @perdu;
break;
case kEquality:
resultString = @galit;
break;
default:
break;
}
}
else
{
switch (clientResult) {
case kWin:
resultString = @perdu;

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 137

137

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

17

138

Communiquer par Bluetooth

break;
case kLose:
resultString = @gagn;
break;
case kEquality:
resultString = @galit;
break;
default:
break;
}
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@Rsultat
message:[NSString stringWithFormat:@Vous
avez %@, resultString]
delegate:nil
cancelButtonTitle:@Ok
otherButtonTitles:nil];
[alert show];
[alert release];
}
Nous avons fini ct serveur. Passons du ct du client, et regardez la Figure 17.2. Dterminer qui est
client/serveur a dj t ralis dans la partie serveur. Ltape suivante est dattendre le top de dpart
pour commencer dcompter. Pour cela, crivez ces lignes en gras :

case NETWORK_SEND_START_SIGNAL:
{
currentCountDown = kStartCounter;

// on a reu le signal de dpart, on commence dcompter


// on lance une premire fois la mthode
[self decrementCount:nil];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:@selector(decrementCount:) userInfo:nil repeats:YES];
}
break;
Une fois que le dcompte arrive zro, il faut notifier le serveur de laction du client pour quil calcule
le rsultat. Pour cela, crivez la mthode du timer :

#pragma mark #pragma mark Timer


- (void) decrementCount:(NSTimer*)theTimer {
labelCountDown.text = [NSString stringWithFormat:@%d, currentCountDown];
// on dcrmente le compteur
currentCountDown --;

01_BlocN_iPhone.indd 138

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

if (currentCountDown == 0)
{
[theTimer invalidate];
theTimer = nil;
labelCountDown.text = @;
// si on est client, on envoie au serveur mon action
if (peerStatus == kClient) {
[self sendNetworkPacket:currentSession packetID:NETWORK_SEND_ACTION
withData:&myGameAction ofLength:sizeof(int) reliable:YES];
}
}
}
La dernire tape ct client est la rception du rsultat pour lafficher :

case NETWORK_RESULT:
{
// on vient de recevoir le rsultat du serveur
int clientResult = pIntData;
[self displayResultForClientResult:clientResult];
}
break;
Noubliez jamais la mthode dealloc :

- (void)dealloc {
[btPicker release];
[currentSession release];
[buttonConnectDisconnect release];
[buttonStartGame release];
[labelServerOrClient release];
[labelCountDown release];
[gamePeerId release];
[super dealloc];
}
Et dclarez dans le .h ces mthodes pour vous dbarrasser des derniers warnings :

- (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID


withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend;
- (void) displayResultForClientResult:(int) clientResult;
- (void) decrementCount:(NSTimer*)theTimer;

17

Communiquer par Bluetooth

01_BlocN_iPhone.indd 139

139

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

18

140

Jouer une vido


18 Jouer une vido

Vous pouvez trs facilement jouer une vido dans votre application, quelle soit locale ou
disponible depuis le Web.
Il y a plusieurs faons de jouer une vido. Nous commencerons par lire une vido prsente sur le
Web, puis une en local, prsente dans le dossier de lapplication. Ensuite, nous verrons comment
ajouter une vue par-dessus, galement appele overlay view. Enfin, nous verrons comment jouer une
vido sans la prsenter en plein cran, une nouveaut !
Les formats de vido accepts sont : .mov, .mp4, .3gp et .mpv avec une compression H.264 ou MPEG-4.
Pour le streaming vido depuis un site Internet, rfrez-vous la fiche Technical Note TN2224 Best
Practices for Creating and Deploying HTTP Live Streaming Media for the iPhone and iPad que vous trouverez dans la documentation.

CRATION

DU PROJET

Commencez par crer un nouveau projet de type Window-based Application que vous nommerez
MoviePlayer. Ajoutez ensuite un RootViewController (fichier de type UIViewController ; cochez
With XIB for User Interface). Puis, ajoutez sa vue dans la fentre, dans lapplication delegate :

MoviePlayerAppDelegate.h
#import <UIKit/UIKit.h>
@class RootViewController;
@interface MoviePlayerAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *rootView;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end

MoviePlayerAppDelegate.m
#import MoviePlayerAppDelegate.h
#import RootViewController.h
@implementation MoviePlayerAppDelegate
// ...
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
rootView = [[RootViewController alloc] initWithNibName:@RootViewController
bundle:nil];

01_BlocN_iPhone.indd 140

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[rootView.view setFrame:[[UIScreen mainScreen] applicationFrame]];


[window addSubview:rootView.view];
[window makeKeyAndVisible];
return YES;
}
// ...
- (void)dealloc {
[window release];
[rootView release];
[super dealloc];
}
Pour utiliser la vido, il faut ajouter le framework MediaPlayer comme suit :
1. Cliquez du bouton droit sur le dossier frameworks de votre application.
2. Cliquez sur Add > Existing Frameworks...
3. Choisissez MediaPlayer.framework.
Nous allons construire linterface graphique ici pour nous concentrer ensuite sur la lecture de la
vido. Dans RootViewController.h, importez le framework MediaPlayer, dclarez un contrleur de
vue de vido (MPMoviePlayerViewController), une vue pour loverlay, un interrupteur pour afficher
ou non la vue doverlay et un contrleur de vido pour faire apparatre la petite vido :

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
@interface RootViewController : UIViewController {
MPMoviePlayerViewController *mPlayer;
UIView *overlayView;
UISwitch *switchOverlay;
MPMoviePlayerController *movieControllerSmall;
}
- (void) playVideoAtUrl:(NSURL*)movieURL;
- (void) presentMovieEmbedded;
- (void) createOverlayView;
@end
Affichons les deux boutons, le label et linterrupteur :

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {


if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
UIButton *localButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[localButton addTarget:self action:@selector(playVideoLocally:)
forControlEvents:UIControlEventTouchUpInside];

18

Jouer une vido

01_BlocN_iPhone.indd 141

141

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

18

142

Jouer une vido

[localButton setTitle:@Local forState:UIControlStateNormal];


[localButton setFrame:CGRectMake(30, 30, 115, 30)];
[self.view addSubview:localButton];
UIButton *webButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[webButton addTarget:self action:@selector(playVideoFromWeb:)
forControlEvents:UIControlEventTouchUpInside];
[webButton setTitle:@Web forState:UIControlStateNormal];
[webButton setFrame:CGRectMake(175, 30, 115, 30)];
[self.view addSubview:webButton];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(30, 90, 260, 30)];
label.textAlignment = UITextAlignmentCenter;
label.text = @Afcher vos propres contrles ?;
[self.view addSubview:label];
[label release];
switchOverlay = [[UISwitch alloc] init];
switchOverlay.center = CGPointMake(160, 140);
[self.view addSubview:switchOverlay];
}
return self;
}
Vous devriez obtenir une interface comme la Figure 18.1.

Figure 18.1 : L interface obtenir.

01_BlocN_iPhone.indd 142

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

JOUER

UNE VIDO DEPUIS LE

WEB

Pour jouer la vido depuis une URL, implmentez tout dabord la mthode suivante (nous ne travaillerons que dans la classe RootViewController) :

- (void) playVideoFromWeb :(id)sender {


[self playVideoAtUrl:[NSURL URLWithString:@http://www.ipup.fr/
livre/Lucky_man_pres.mov]];
}
Ensuite, nous allons dfinir la mthode playVideoAtUrl: :

- (void) playVideoAtUrl:(NSURL*)movieURL {
if(!mPlayer)
mPlayer = [[MPMoviePlayerViewController alloc] init];
mPlayer.moviePlayer.contentURL = movieURL;
// on met la vido en plein cran
mPlayer.moviePlayer.fullscreen = YES;
// on afche la vido
[self presentMoviePlayerViewControllerAnimated:mPlayer];
// on joue
[mPlayer.moviePlayer play];
}
On nalloue le lecteur quune fois au premier appel de la mthode, pour optimiser les performances.
Utiliser la mthode presentMoviePlayerViewControllerAnimated: permet dafficher la vido avec
animation. De plus, lorsque la vido est termine, ou lorsquon clique sur le bouton Terminer, la vue
est automatiquement enleve. Cependant, rien ne vous empche dafficher la vue de mPlayer comme
vous le souhaitez. Il faudra vous abonner aux notifications de fin de lecture de la vido pour enlever
la vue : MPMoviePlayerPlaybackDidFinishNotication.

Info
Si vous souhaitez optimiser les performances (surtout si la vido est longue), tlchargez la vido
dans un premier temps, puis jouez l une fois que le tlchargement est termin.

JOUER

UNE VIDO EN LOCAL

Pour jouer une vido en local, rien de plus simple. Il faut tout dabord copier la vido dans votre
projet, puis implmenter cette mthode :

- (void) playVideoLocally :(id)sender {


NSString *path = [[NSBundle mainBundle] pathForResource:@Lucky_man_pres
ofType:@mov];
if (path) {
[self playVideoAtUrl:[NSURL URLWithString:path]];
}
}

18

Jouer une vido

01_BlocN_iPhone.indd 143

143

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

18

144

Jouer une vido

Vous pouvez rcuprer la vido en ligne depuis votre navigateur en tapant ladresse employe pour
jouer la vido depuis le Web.

A JOUTER

UNE VUE PAR - DESSUS

Vous pouvez dfinir une vue ajouter par-dessus votre vido, pour afficher certaines informations, ou
pour prsenter vos propres contrles. Nous allons donc ajouter ici une vue si linterrupteur est
slectionn. Cette vue contiendra un bouton qui permettra de jouer/mettre en pause la vido. Sinon,
nous laisserons les contrles par dfaut. Ajoutez les lignes en gras :

- (void) playVideoAtUrl:(NSURL*)movieURL {
if(!mPlayer)
mPlayer = [[MPMoviePlayerViewController alloc] init];
mPlayer.moviePlayer.contentURL = movieURL;
// on met la vido en plein cran
mPlayer.moviePlayer.fullscreen = YES;

if (switchOverlay.on)
{
// on ajoute la vue doverlay
[self createOverlayView];
if ([overlayView superview] == nil) {
[mPlayer.moviePlayer.view addSubview:overlayView];
}
// on enlve les controls pour mettre les ntres
mPlayer.moviePlayer.controlStyle = MPMovieControlStyleNone;
}
else
{
// on lenlve
[overlayView removeFromSuperview];
// on mets les contrles par dfaut en mode plein cran
mPlayer.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
}
// on afche la vido
[self presentMoviePlayerViewControllerAnimated:mPlayer];
// on joue
[mPlayer.moviePlayer play];
}
Puis dfinissez la mthode permettant de crer la vue. L encore, nous ne crons la vue que si elle
nexiste pas :

- (void) createOverlayView {
// si elle existe dj, on quitte
if (overlayView) {
return;
}

01_BlocN_iPhone.indd 144

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// sinon, on la cre
overlayView = [[UIView alloc] initWithFrame:self.view.frame];
// on peut dnir une opacit
overlayView.alpha = 0.8;
UIButton *pauseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[pauseButton addTarget:self action:@selector(pauseResumeVideo:)
forControlEvents:UIControlEventTouchUpInside];
[pauseButton setTitle:@Pause forState:UIControlStateNormal];
[pauseButton setTitle:@Resume forState:UIControlStateSelected];
[pauseButton setFrame:CGRectMake(175, 30, 115, 30)];
[overlayView addSubview:pauseButton];
}
Enfin, il faut implmenter la mthode pour jouer/mettre en pause avec le bouton :

- (void) pauseResumeVideo :(id)sender {


UIButton *but = (UIButton*)sender;
if (but.selected) {
[mPlayer.moviePlayer play];
}
else {
[mPlayer.moviePlayer pause];
}
// on change ltat du bouton
but.selected = !but.selected;
}
Vous pouvez lancer la vido, en choisissant dafficher ou non la vue doverlay !

JOUER

UNE VIDO DANS UN COIN DE LCRAN

Il est possible de jouer une vido sans obligatoirement la prsenter en plein cran depuis iOS3.2. Pour
cela, il faut crer une vue qui servira de conteneur pour lobjet permettant de jouer la vido (MPMoviePlayerController).

Info
Il nest pas conseill davoir plusieurs lecteurs vido dans une mme vue. Nous le faisons ici pour le
besoin de cette fiche et ne pas avoir recrer un nouveau projet.
La vido apparatra au lancement de lapplication, afin de la jouer en boucle. Dans ce but, ajouter la
fin du initWithNibName:bundle: :

[self presentMovieEmbedded];

18

Jouer une vido

01_BlocN_iPhone.indd 145

145

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

18

146

Jouer une vido

Ensuite, implmentez-la :
- (void) presentMovieEmbedded {
// la vue qui va contenir notre lecteur
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(30, 200, 260, 200)];

// initialisation du contrleur
movieControllerSmall = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL
URLWithString:@ http://www.ipup.fr/livre/Lucky man pres.mov]];
// on dnit sa taille
movieControllerSmall.view.frame = CGRectMake(0, 0, 260, 200);
// on dnit la couleur du fond
movieControllerSmall.backgroundView.backgroundColor = [UIColor whiteColor];
// on dnit une rption de la lecture lorsque la vido est nie
movieControllerSmall.repeatMode = MPMovieRepeatModeOne;
// on ajoute le player
[myView addSubview:movieControllerSmall.view];
// on ajoute la vue
[self.view addSubview:myView];
// on lance la lecture
[movieControllerSmall play];
[myView release];
}
La vido va sadapter la taille de la vue qui sert de conteneur. De plus, lorsque vous cliquerez sur le
bouton pour bnficier du plein cran, la vido saffichera automatiquement dans tout lcran disponible. L aussi, vous pouvez vous abonner certaines notifications pour ragir lvnement : Met la
vue en plein cran. Ainsi vous pourrez arrter certaines actions dans la vue comme une animation.
Noubliez pas le dealloc :
- (void)dealloc {
[movieControllerSmall release];
[mPlayer release];
[overlayView release];
[switchOverlay release];

[super dealloc];
}

01_BlocN_iPhone.indd 146

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

19

Les notifications Push


19 Les notifications Push

Depuis la sortie de iOS3, il est possible denvoyer des notifications Push un iPhone, un
iPad ou un iPod Touch qui dispose dune connexion Internet. Pour lutilisateur, une notification se matrialise la plupart du temps comme un SMS reu de la part dune application installe sur son mobile. Lintrt principal est dinformer lutilisateur dvnements,
mme si lapplication nest pas lance ce moment-l.Typiquement une notification peut
avertir dune nouvelle importante comme une alerte mto ou un but de lquipe de
France de football.
Nous allons voir tape par tape comment raliser ce type dapplication.

ENREGISTREMENT

DANS LE PORTAIL DES PROVISIONINGS

Il y a beaucoup de choses faire sur le portail des provisionings dApple afin dexploiter le Push dans
votre application. Il faut commencer par crer un nouvel identifiant dapplication :
Provisioning portal > app IDs > New App ID.
Figure 19.1 :
Crez un nouvel
App ID.

Il suffit de remplir les champs proposs. Par contre, et cest important, il faut utiliser un Bundle Identifier explicite cest--dire sans *.

Figure 19.2 :
Dcrivez lApp ID.

On aboutit la Figure 19.3.


Figure 19.3 : Votre
nouvel App ID !

19

Les notifications Push

01_BlocN_iPhone.indd 147

147

18/08/10 12:48

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

19

148

Les notifications Push

Passons maintenant la configuration. Il faut donc cocher la case Enable for Apple Push Notification
Service. Ensuite, deux certificats sont configurer. Le premier est un certificat de dveloppement qui
va nous permettre de tester notre application sur un iPhone pendant le dveloppement de notre
application. Le second est un certificat de Production qui enverra des notifications des applications
tlcharges partir de lApp Store.
Nous allons configurer les deux ! Pour le premier :

Info
ce stade, je vous conseille de crer un dossier tuto_push car nous allons crer plusieurs fichiers.
1. Configurez le certificat de dveloppement en cliquant sur Configure.
2. En parallle, lancez le trousseau daccs (utilisez le Finder par exemple) et allez dans Trousseau
daccs > Assistant de certification > Demander un certificat une autorit de certificat.

Figure 19.4 :
Le chemin pour
demander le certificat.

3. Remplissez les champs demands (sauf Adresse lectronique de lAC qui nest pas ncessaire)
et choisissez Enregistrer sur le disque.
4. Enregistrez la demande dans le dossier tuto_push sous le nom : CertificateSigningRequestDev.
certSigningRequest
Cest termin pour le trousseau daccs. Retournez sur le portail des provisionings dApple puis :
1. Cliquez sur Continue.
2. Choisissez le fichier que lon vient de crer.
3. Cliquez sur Generate puis choisissez Continue.
4. Enfin, il reste tlcharger le certificat cr et le mettre dans le dossier tuto_push pour ne
pas le perdre.
5. Procdez de la mme manire avec le Certificat de Production en crant un
CertificateSigningRequestProd.certSigningRequest grce au trousseau daccs.
ce stade vous devriez avoir quatre fichiers dans le dossier tuto_push :

aps_developer_identity.cer
aps_production_identity.cer
CertificateSigningRequestDev.certSigningRequest
CertificateSigningRequestProd.certSigningRequest

01_BlocN_iPhone.indd 148

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Passons maintenant la cration des provisionings.


1. Dans la rubrique Provisioning, onglet Development, cliquez sur New Profile.
2. Renseignez le champ Profile Name avec Tuto Push et cochez le certificat adquat.
3. Slectionnez lApp ID tuto push, puis les appareils sur lesquels vous testerez votre application. Il
reste soumettre le provisoning, le tlcharger et le ranger dans le dossier tuto_push.
4. Il faut recommencer lopration dans longlet Distribution pour crer le provisioning vous
permettant de compiler votre application pour sa distribution sur lApp Store.
Vous avez maintenant six fichiers dans le dossier tuto_push et toutes les oprations demandes par
Apple pour les certificats sont ralises. Nous allons voir comment exploiter tous ces fichiers.

PRPARATION

DES CERTIFICATS POUR LE SERVEUR

ce stade, les possibilits sont nombreuses, et nous nallons pas toutes les dtailler. Par contre, vous
apprendrez envoyer des push partir dun serveur quelconque et pour cela, nous allons devoir
raliser des oprations sur les certificats crs prcdemment.
Tout dabord, double-cliquez sur le fichier aps_developer_identity.cer puis retournez dans le trousseau daccs rubrique Session et dans Certificats. Vous devriez trouver un nouveau certificat install
de type Apple Production Push Service.
En double cliquant dessus, vous devriez retrouver les informations prsentes Figure 19.5.

Figure 19.5 : Votre certificat


Apple Production Push Service.

Il faut alors slectionner le Certificat et la cl puis cliquer du bouton droit, choisir Exporter 2
lments et enregistrer dans tuto_push sous le nom CertificatsDev.p12.

Figure 19.6 : La liste de choix aprs avoir


cliqu du bouton droit.

Il vous est alors demand un premier mot de passe qui sert protger votre cl p12. Puis, on vous
demande votre mot de passe de session. Faites bien attention de ne pas vous tromper et souvenezvous imprativement de ces mots de passe.

19

Les notifications Push

01_BlocN_iPhone.indd 149

149

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

19

150

Les notifications Push

Rptez lopration avec le certificat de Production en nommant lexport CertificatsProd.p12.


Le dossier tuto_push compte maintenant huit fichiers :

aps_developer_identity.cer
aps_production_identity.cer
CertificateSigningRequestDev.certSigningRequest
CertificateSigningRequestProd.certSigningRequest
Tuto_Push.mobileprovision
Tuto_Push_Prod.mobileprovision
CertificatsDev.p12
CertificatsProd.p12

Nous devons maintenant convertir les fichiers .p12 en .pem pour les utiliser avec le code PHP que
nous allons bientt faire.
Commencez par crer un petit script Shell qui va simplifier les oprations. Crez un nouveau fichier
convert.sh dans le dossier tuto_push avec votre diteur de texte prfr et ajoutez-y ces lignes de
commandes :
#traitement du certicat de dveloppement
openssl pkcs12 -clcerts -nokeys -out CerticatDev.pem -in CerticatsDev.p12
openssl
pkcs12 -nocerts -out KeyDev.pem -in CerticatsDev.p12
cat CerticatDev.pem KeyDev.pem > apns-dev.pem

#traitement du certicat de Production


openssl pkcs12 -clcerts -nokeys -out CerticatProd.pem -in CerticatsProd.p12
openssl
pkcs12 -nocerts -out KeyProd.pem -in CerticatsProd.p12
cat CerticatProd.pem KeyProd.pem > apns-prod.pem
#vrication
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert apns-dev.pem
openssl s_client -connect gateway.push.apple.com:2195 -cert apns-prod.pem
Nous sommes prsent prts convertir nos .p12 en .pem. Pour cela nous allons lancer le terminal
par le Finder. Le terminal est rang dans le dossier Utilitaires de vos applications.
Utilisez la commande cd pour vous positionner dans le dossier tuto_push. Si le dossier tuto_push est
sur le bureau : cd Desktop/tuto_push.

Info
Les commandes basiques ncessaires pour naviguer dans la hirarchie de vos fichiers sont rappeles
la Fiche 31.
Il vous reste lancer le script en crivant dans la fentre du terminal : sh convert.sh

01_BlocN_iPhone.indd 150

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Le script va se lancer et vous poser deux types de question :

Enter Import Password. Il faut donc entrer votre mot de passe dexport de cl p12 puis valider
(touche Enter).

Enter PEM pass phrase. On vous demande un mot de passe qui protgera votre certificat pem.
Une fois ces oprations effectues, votre dossier tuto_push doit contenir quinze fichiers dont les
prcieux ssames que sont apns-dev.pem et apns-prod.pem.
Beaucoup de ressources sur Internet proposent denlever la cl des certificats pem grce aux
commandes suivantes :

#traitement du certicat de dveloppement


openssl pkcs12 -clcerts -nokeys -out CerticatDev.pem -in CerticatsDev.p12
openssl
pkcs12 -nocerts -out KeyDev.pem -in CerticatsDev.p12
openssl rsa -in KeyDev.pem -out NoKeyDev.pem
cat CerticatDev.pem NoKeyDev.pem > apns-dev.pem
#traitement du certicat de Production
openssl pkcs12 -clcerts -nokeys -out CerticatProd.pem -in CerticatsProd.p12
openssl
pkcs12 -nocerts -out KeyProd.pem -in CerticatsProd.p12
openssl rsa -in KeyProd.pem -out NoKeyProd.pem
cat CerticatProd.pem NoKeyProd.pem > apns-prod.pem
#vrication
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert apns-dev.pem
openssl s_client -connect gateway.push.apple.com:2195 -cert apns-prod.pem
La scurit est plus faible : il suffirait de drober le fichier pem pour envoyer des push vos utilisateurs. Toutefois, omettre cette scurit simplifie lgrement la programmation serveur. Nous allons
donc continuer avec nos certificats pem protgs par notre pass phrase. Nous pouvons maintenant
faire un peu de programmation, enfin !

APPLICATION

POUR LIPHONE

Pour commencer, crez un nouveau projet de type Window-based Application et nommez-le Tuto_push.
Dans Tuto_pushAppDelegate.m nous allons enregistrer lapplication aux notifications push de la
manire suivante :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//enregistrement aux notications Push
[application registerForRemoteNoticationTypes: UIRemoteNoticationTypeAlert
| UIRemoteNoticationTypeBadge | UIRemoteNoticationTypeSound];
[window makeKeyAndVisible];
return YES;
}

19

Les notifications Push

01_BlocN_iPhone.indd 151

151

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

19

152

Les notifications Push

De plus, il y a deux mthodes implmenter qui vont permettre de rcuprer le Token ou lerreur.

-(void)application:(UIApplication *)application
didRegisterForRemoteNoticationsWithDeviceToken:(NSData *)deviceToken {
NSLog(@enregistrement russi) ;
NSLog(@%@, deviceToken);
}
-(void)application:(UIApplication *)application
didFailToRegisterForRemoteNoticationsWithError:(NSError *)error{
NSLog(@chec de lenregistrement) ;
}
Il reste maintenant installer et slectionner le provisioning de dveloppement pour compiler notre
application correctement. Pour ce faire :
1. Dans le dossier tuto_push, double-cliquez sur Tuto_Push.mobileprovision.
2. Puis, dans Xcode, choisissez la configuration Debug sur Device.
3. Ensuite, allez dans Project > Edit Project Settings et remplissez le champ Code Signing Identity
avec le provisioning de dveloppement pour le Bundle identifier tutopush (voir Figure 19.7).

Figure 19.7 : Choisissez le bon provisioning pour la compilation.


4. Enfin, noubliez pas de remplir correctement le Bundle identifier dans le .plist.
Ces oprations finies, vous pouvez compiler et lancer lapplication sur votre iPhone (cela ne fonctionne pas sur simulateur). Dans la console, vous devriez voir apparatre votre token sous la forme :

Tuto_push[6914:307] enregistrement russi


Tuto_push[6914:307] <2b555730 5a83a7cf c216b51a 0df488ec 6bc61168 0ac2078d
5ca8111b 28f428c7>
Gardez le message de la console de ct, nous allons bientt en avoir besoin. Lessentiel de la
programmation ct iPhone est maintenant termin, passons au serveur !

PROGRAMMATION

DU SERVEUR

Nous allons crire du code en PHP afin denvoyer un push lapplication que nous venons de terminer.
Pour cela, votre serveur doit tre capable dutiliser les sockets en PHP (voir votre php.ini) et surtout
avoir le port 2195 ouvert ( voir avec votre hbergeur en lisant la documentation mise disposition
ou en le contactant directement).
Comme tout le monde ne dispose pas dun serveur ddi, et quun simple hbergement nest pas
suffisant, nous allons recourir un serveur local sur Mac. Deux cas de figure : soit vous avez dj fait
du PHP sur Mac et il ny a pas de problme, soit vous ne matrisez pas PHP et je vous conseille
dinstaller MAMP et de procder quelques tests de script PHP.
Nous allons crer un nouveau dossier php_push pour y placer notre code PHP. Pour moi, ce dossier
est accessible ladresse : http://localhost:8888/php_push/

01_BlocN_iPhone.indd 152

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Dans ce dossier, nous avons besoin de trois fichiers :

Un fichier avec notre code php (pour moi sample.php).


Une copie du fichier apns-dev.pem se trouvant dans tuto_push et renomm tutopush-dev.pem.
Une copie du fichier apns-prod.pem se trouvant dans tuto_push et renomm tutopush-prod.pem.
Passons au code PHP :
Tout dabord, avec la class push fournie, la seule modification apporter de votre part est le changement de passphrase par votre mot de passe pem.
<?php

class Push
{
//serveur de dveloppement
private $apnsHostDev = gateway.sandbox.push.apple.com;
//serveur de Production
private $apnsHostProd = gateway.push.apple.com;
//port de connection
private $apnsPort = 2195;
//mot de passe de de votre pem
private $passPhrase = passphrase;
//on travaille en local
private $serviceHost = localhost;
private $apnsConnection;
public function __construct ($cert, $mode) {
//connexion chez apple
$streamContext = stream_context_create();
//on donne ladresse du .pem ici cela donne tutopush-dev.pem
stream_context_set_option($streamContext, ssl, local_cert,
$cert.-.$mode..pem);
//on renseigne le mot de passe
stream_context_set_option($streamContext, ssl, passphrase,
$this->passPhrase);
// en mode dev on utilise le serveur de dveloppement
if($mode == dev)
$this->apnsConnection =
stream_socket_client(ssl://.$this->apnsHostDev.:.$this->apnsPort,
$error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);
// en mod prod on utilise le serveur de production

19

Les notifications Push

01_BlocN_iPhone.indd 153

153

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

19

154

Les notifications Push

if($mode == prod)
$this->apnsConnection =
stream_socket_client(ssl://.$this->apnsHostProd.:.$this->apnsPort,
$error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);
}
public function __destruct () {
//fermeture de la connexion
fclose($this->apnsConnection);
}
public function sendMessageToToken ($token, $message)
{
//envoi du push
// cration du message en JSON partir du tableau associatif
$message = json_encode($message);
//formatage du message
$apnsMessage = chr(0) . chr(0) . chr(32) . pack(H*, str_replace( , ,
$token)) . chr(0) . chr(strlen($message)) . $message;
//criture dans la socket
fwrite($this->apnsConnection, $apnsMessage);
}
}
?>
Cette classe se dcompose en trois parties principales :

Ouverture de la connexion socket vers le service de push dApple.


criture dun message un appareil (on peut envoyer plusieurs messages travers une seule
connexion socket pour plus defficacit).

Fermeture de la connexion socket.


prsent, regardons comment exploiter cette classe travers un exemple :

<?php
// copiez ici le token rcupr dans la console et respectant le format propos
$token
= 2b555730 5a83a7cf c216b51a 0df488ec 6bc61168 0ac2078d 5ca8111b 28f428c7;
//cration dune instance push pour lapplication tutopush en dveloppement
$push = new Push(tutopush, dev);
//En production on utiliserait
//
$Push = new SendPush(tutopush, prod);
//tableau associatif contenant le message Push, le son et le badge
$payload[aps] = array(alert => Le message passer, sound => default,
badge=> 100);
//envoi du message

01_BlocN_iPhone.indd 154

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

$push->sendMessageToToken ($token, $payload);


// cest ici que lon pourrait ajouter des destinataires
//$push->sendMessageToToken ($token2, $payload);
//$push->sendMessageToToken ($token2, $payload);
// fermeture de la socket
unset($push);
?>
Une fois tout ce code ajout votre fichier sample.php, il vous reste lexcuter via votre navigateur
(http://localhost:8888/php_push/sample.php) ou via votre terminal et vous devriez recevoir un Push !

Figure 19.8 : La rception


de votre premier push !

Mme si vous avez reu votre fameux Push, ce nest quun dbut. En effet, pour raliser un service
complet, vous devez continuer limplmentation de la mthode didRegisterForRemoteNoticationsWithDeviceToken: afin dy inclure une requte vers votre serveur (ou votre ordinateur en WiFi
par exemple) pour lui envoyer votre token.
Mais, et vous vous en tes peut-tre rendu compte, le vrai dfi avec le push est ct serveur et ce
nest pas forcment ni de votre ressort, ni de celui de ce livre de raliser ce service complet. Cest la
raison pour laquelle je vous conseille dutiliser une des solutions suivantes, qui vous feront gagner du
temps :

Tout dabord, easyapns (http://www.easyapns.com) qui fournit une solution assez complte de
gestion de push en PHP.

Il existe galement lquivalent en java (http://github.com/notnoop/java-apns).


Ou encore, et cest peut-tre le plus simple : appnotify (http://appnotify.com).

19

Les notifications Push

01_BlocN_iPhone.indd 155

155

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

20

156

Utiliser des photos


20 Utiliser des photos

LiPhone possde un capteur pour prendre des photos. Dans iOS, il est possible de
travailler avec des photos, que ce soit directement partir de lappareil photo numrique
ou partir de lalbum photo. Cet album est disponible pour lutilisation dans une application.
Dans cette fiche, nous allons apprendre manipuler lappareil photo, choisir une photo, lafficher dans
une application, dessiner par-dessus et sauvegarder le rsultat. Pour ajouter un peu de piment, nous
effacerons le dessin en secouant lappareil !
Pas dinquitude, nous procderons par tapes !
Nous obtiendrons le rsultat comme la Figure 20.1. La Figure 20.2 montre limage telle que sauvegarde ensuite.

Figure 20.1 : Une capture cran de lapplication.

01_BlocN_iPhone.indd 156

Figure 20.2 : L image obtenue la fin.

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

CRER

UN PROJET ET AFFICHER LES IMAGES

Pour commencer, crez un nouveau projet de type View-based Application que vous nommerez
PlayWithPhoto.
Le but de lapplication tant galement de faire une capture cran, elle sera plus jolie si nous cachons
la barre de statut (o saffiche le niveau de la batterie, le nom de loprateur...). Afin de la cacher,
il faut diter le fichier PlayWithPhoto-Info.plist et ajouter une cl : Status bar is initially hidden puis
cocher la case qui apparat.
On affiche lappareil photo ou lalbum photo grce un objet de la classe UIImagePickerController.
Aprs avoir choisi la photo, il faudra lafficher. Cet affichage se fera en plein cran, grce un objet de
la classe UIImageView. Dclarez dans PlayWithPhotoViewController.h :

UIImageView *imageViewBackgroundPhoto;
Puis, dans le viewDidLoad, allouez cette image view :

// la vue qui afchera limage en fond


imageViewBackgroundPhoto = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0,
320, 480)];
// on ne met pas dimage ici
[self.view addSubview:imageViewBackgroundPhoto];
La vue pour prendre la photo saffichera en cliquant sur un bouton. Crez donc ce bouton dans le
viewDidLoad du PlayWithPhotoViewController.m :

// bouton pour prendre la photo


UIButton *buttonTakePhotoFromCamera =
[UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonTakePhotoFromCamera setFrame:CGRectMake(30, 430, 80, 30)];
[buttonTakePhotoFromCamera addTarget:self action:@selector(showCameraPicker:)
forControlEvents:UIControlEventTouchUpInside];
[buttonTakePhotoFromCamera setTitle:@Photo forState:UIControlStateNormal];
[self.view addSubview:buttonTakePhotoFromCamera];
Puis, crivez la mthode appele par ce bouton :

#pragma mark #pragma mark action methods


- (void) showCameraPicker:(id)sender {
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera; 
picker.delegate = self; 
picker.allowsEditing = NO; 
//on afche le picker
[self presentModalViewController:picker animated:YES];
}

20

Utiliser des photos

01_BlocN_iPhone.indd 157

157

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

20

158

Utiliser des photos

Ligne , on slectionne le type de donnes afficher grce lobjet picker de la classe UIImagePickerController. Ici, le but tant de permettre lutilisateur de prendre une photo, on spcifie UIImagePickerControllerSourceTypeCamera.
Ligne , self est dlgu. Il faut ajouter dans le .h :

@interface PlayWithPhotoViewController : UIViewController


<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
Ligne , on peut choisir de laisser lutilisateur modifier limage avant de sen servir dans lapplication.
Ici, on ne lautorise pas. Figure 20.3, vous trouverez la vue affiche lcran lorsque lon autorise
lutilisateur modifier limage en mettant YES.

Figure 20.3 : Une vue pour l dition.

Aprs avoir affiche la vue modale pour prendre la photo, on peut soit annuler, soit choisir limage.
Pour le premier cas, il faut crire :

#pragma mark #pragma mark image picker delegate


- (void) imagePickerControllerDidCancel:(UIImagePickerController *)picker {
// on enlve la vue
[picker dismissModalViewControllerAnimated:YES];
// on release le picker
[picker release];
}

01_BlocN_iPhone.indd 158

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Lorsque lutilisateur choisit limage, il faudra lafficher dans lapplication :

- (void) imagePickerController:(UIImagePickerController *)picker


didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo {
imageViewBackgroundPhoto.image = image;
// on enlve la vue
[picker dismissModalViewControllerAnimated:YES];
// on release le picker
[picker release];
}

Info
Dans le cas o vous permettez lutilisateur de modifier limage, le paramtre image contiendra
limage modifie, editingInfo contiendra lui limage originale et le rectangle qui dlimite limage
modifie dans limage originale.

Attention
Vous devez tester si lappareil peut prendre des photos, ou afficher lalbum. Pour cela, nous
choisissons ici dafficher ou non le bouton correspondant laction disponible ou non. Ajoutez dans
le viewDidLoad :

// si on ne peut pas prendre de photo, on cache le bouton


if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceT
ypeCamera])
buttonTakePhotoFromCamera.hidden = YES;
Pour afficher lalbum photo, crez le bouton dans le viewDidLoad :

// bouton pour prendre une photo depuis lalbum


UIButton *buttonTakePhotoFromAlbum = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonTakePhotoFromAlbum setFrame:CGRectMake(120, 430, 80, 30)];
[buttonTakePhotoFromAlbum addTarget:self action:@selector(showPhotoAlbumPicker:)
forControlEvents:UIControlEventTouchUpInside];
[buttonTakePhotoFromAlbum setTitle:@Album forState:UIControlStateNormal];
[self.view addSubview:buttonTakePhotoFromAlbum];
// si on ne peut pas accder lalbum photo, on cache le bouton
if (![UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum])
buttonTakePhotoFromAlbum.hidden = YES;

20

Utiliser des photos

01_BlocN_iPhone.indd 159

159

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

20

160

Utiliser des photos

La mthode showPhotoAlbumPicker: est la suivante :

- (void) showPhotoAlbumPicker:(id)sender {
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
picker.delegate = self;
picker.allowsEditing = NO;
//on afche le picker
[self presentModalViewController:picker animated:YES];
}

DESSINER

PAR - DESSUS LIMAGE

Dans cette partie, laissons libre court limagination de lutilisateur en lui permettant de toucher la
photo pour y dposer une image. Cette image, nomme ici shot.png a t trouve sur le site http://
www.iconfinder.com. Elle fait 32 32 pixels, noubliez pas dadapter la taille de funnyImage.
Limage sera charge une seule fois lors du premier affichage de la vue. Pour cela, dclarez dans le .h :

UIImage *funnyImage;
Puis, dans le viewDidLoad :
// trouve sur http://www.iconnder.com Auteur Everaldo Coelho
funnyImage = [[UIImage imageNamed:@shot.png] retain];
Il est trs facile de dtecter un appui de lutilisateur. Il suffit dimplmenter les mthodes :
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// appel lorsque lutilisateur pose son doigt
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// appel tout au long du dplacement du doigt sur lcran
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// appel lorsque lutilisateur relve le doigt
}
Ces mthodes permettent de dtecter le multitouch :

int nbOfFingers = [touches count]; // retourne le nombre de doigt sur lcran


et galement de compter le nombre de taps conscutifs (lorsquils sont rapides) :

touch.tapCount;

01_BlocN_iPhone.indd 160

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Nous nous proccuperons ici de la mthode appele lorsque lutilisateur pose son doigt, et afficherons une nouvelle image :

#pragma mark #pragma mark Gestion du touch


- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
// on rcupre les coordonnes dans la vue
CGPoint touchCoordinates = [touch locationInView:self.view];
// on cre une nouvelle image view
UIImageView *shot = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
shot.image = funnyImage;
// on centre par rapport au doigt
shot.center = touchCoordinates;
[self.view addSubview:shot];
[shot release];
}

SAUVEGARDER LIMAGE
Aprs stre amus en ajoutant beaucoup de seringues (dans mon exemple) sur la photo, vous pouvez
proposer lutilisateur de la sauvegarder dans son album. Commencez par crer le bouton qui ralisera laction en ajoutant dans le viewDidLoad :

// bouton pour sauver la photo


UIButton *buttonSavePhoto = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonSavePhoto setFrame:CGRectMake(210, 430, 80, 30)];
[buttonSavePhoto addTarget:self action:@selector(savePhoto:)
forControlEvents:UIControlEventTouchUpInside];
[buttonSavePhoto setTitle:@Sauver forState:UIControlStateNormal];
[self.view addSubview:buttonSavePhoto];
Ensuite, nous allons nous occuper de la mthode appele lors de lappui sur le bouton :

- (void) savePhoto:(id)sender {
}
Prendre une capture de lcran ncessite dabord de cacher les boutons. Pour cela, on numre tous
les objets de la classe UIView dans les sous-vues de la vue du contrleur (on numre toutes ses
sous-vues) puis, si la vue nest pas de la classe UIImageView, on la cache. Ajoutez :

// on va prendre une capture cran


// on cache toutes les vues sauf les image view
for (UIView *vi in [self.view subviews])
if (![vi isKindOfClass:[UIImageView class]])
[vi setHidden:YES];

20

Utiliser des photos

01_BlocN_iPhone.indd 161

161

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

20

162

Utiliser des photos

Puis, on peut prendre une capture cran de ce qui saffiche :

// on fait la capture cran


CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.view.layer renderInContext:ctx];
UIImage *capturedimage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Enfin, on sauvegarde cette image dans lalbum photo :

UIImageWriteToSavedPhotosAlbum(capturedimage, self,
@selector(image:didFinishSavingWithError:contextInfo:), nil);
la fin de lenregistrement dans lalbum, la mthode spcifie (o plutt le selector) dans le troisime paramtre de la fonction ci-dessus sera appele. On raffichera donc les boutons :

#pragma mark #pragma mark sauvegarde


- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *) error
contextInfo:(void *)contextInfo {
NSLog(@Image sauve);
// on remet tous les boutons
for (UIView *vi in [self.view subviews])
vi.hidden = NO;
}

SECOUER

POUR EFFACER

la Fiche 15, je vous ai prsent comment utiliser lacclromtre pour dtecter un mouvement de
secousse. Il existe une manire plus simple pour dtecter ce type dvnements. En fait, il faut choisir
le contrleur qui rceptionnera les vnements. Ajoutez ces lignes :

#pragma mark #pragma mark gestion de la secousse


- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)viewDidAppear:(BOOL)animated {
[self becomeFirstResponder];
}

01_BlocN_iPhone.indd 162

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ensuite, la rception dun vnement appellera ces mthodes :

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event


{
// appel lorsque lvnement dbute
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
// appel lorsque lvnement est termin
}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
// appel lorsque lvnement est annul
}
Dans notre cas, nous dtecterons lvnement UIEventSubtypeMotionShake. Il en existe dautres qui
ont trait aux vnements lis la lecture de laudio/vido. Il vous faudra regarder le type numr
UIEventSubtype dans la documentation.
Ici, lorsque la secousse sera dtecte, on enlvera toutes les vues de la classe UIImageView sauf celle
qui contient limage originale de fond :

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event


{
if (event.subtype == UIEventSubtypeMotionShake) {
// on fait une copie des sous vues de la vue du contrleur
NSArray *arrayOfSubviews = [self.view subviews];
// on numre toutes les image view
for (UIView *vi in arrayOfSubviews)
{
// si ce sont des seringues, on les enlve
if ([vi isKindOfClass:[UIImageView class]])
if (((UIImageView*)vi).image == funnyImage)
[vi removeFromSuperview];
}
}
}
Noubliez pas la mthode dealloc !

- (void)dealloc {
[imageViewBackgroundPhoto release];
[funnyImage release];
[super dealloc];
}

20

Utiliser des photos

01_BlocN_iPhone.indd 163

163

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

164

LIn App Purchase (achat intgr)


21 LIn App Purchase (achat intgr)

O comment intgrer de lachat de contenu dans votre application.


Lorsque vous distribuez votre application sur lApp Store, quelle soit gratuite ou non, vous pouvez
demander lutilisateur dy acheter du contenu. Un moyen trs pratique dans les cas suivants par
exemple :

Votre application propose le contenu dun journal. Vous pouvez demander lutilisateur de
sabonner au journal moyennant finances pour continuer bnficier de votre application.

Vous avez dvelopp un jeu, et souhaitez proposer du contenu en plus comme lachat de vies, de
niveaux, darmes.

Votre application est initialement gratuite et bride. Vous pouvez demander lutilisateur de la
dbrider en payant via lIn App Purchase.
Vous avez certainement plein dides ! Ce systme, trs pratique, permet de faire voluer votre application en fonction des dsirs de lutilisateur.

Info
Comme pour la vente dapplications, Apple garde 30 % du prix de vente et vous redistribue les 70 %
restants.
Lajout de lIn App Purchase dans votre application peut se rvler fastidieux car il faut crer un provisioning pour chaque application distribue, intgrant de lIn App Purchase. Mais ne vous dcouragez pas !
Nous verrons dans un premier temps comment configurer le provisioning pour disposer de lIn App
Purchase dans notre application, puis nous construirons une classe dans un nouveau projet permettant de grer le store. Enfin, nous ajouterons des objets acheter, puis nous verrons comment tester
lapplication sans dbourser un seul centime !
Notez que jentends par store llment dans votre application faisant linterface entre les serveurs
Apple et votre application, pour vous distribuer les lments disponibles en In App Purchase.

Attention
Vous devez avoir achet la licence pour dvelopper sur iPhone afin de pouvoir raliser cette fiche.

CRATION

DU PROVISIONING

Il est primordial de bien suivre lordre des tapes, et si possible, de remplir les mmes donnes des
exemples pour ne pas sy perdre. Nous allons crer un provisioning pour dvelopper notre application de test In App Purchase.
Rendez-vous sur le site http://developer.apple.com/iphone/ et loguez-vous. Ensuite, dirigez-vous
vers iPhone Provisioning Portal puis sur App IDs. Cliquez sur New App ID comme la Figure 21.1.

01_BlocN_iPhone.indd 164

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 21.1 : Crez


une nouvelle
App ID.

Remplissez les informations comme la Figure 21.2. Aprs avoir cliqu sur Submit, vous devriez
retrouver une ligne comme la Figure 21.3 (except le numro avant .com).

Figure 21.2 :
Rentrez les
informations
de lApp ID.

Figure 21.3 : Votre


nouvelle App ID.
Cliquez sur Configure puis activez lIn App Purchase comme la Figure 21.4. Cliquez sur Done. De
retour la liste des App IDs, vous devriez avoir le bouton vert dans la colonne In App Purchase pour
votre App ID.

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 165

165

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

166

LIn App Purchase (achat intgr)

Figure 21.4 :
Activez l In App
Purchase.

Maintenant, rendez-vous dans Provisioning (juste en dessous de App IDs) puis cliquez sur New Profile.
Rentrez le nom comme la Figure 21.5, puis choisissez les dveloppeurs concerns par ce provisioning
(choix des certificats) puis les appareils concerns et enfin, choisissez lApp ID que nous venons de
crer. Vous devriez obtenir quelque chose danalogue la Figure 21.5.

Figure 21.5 : Remplissez


les donnes pour
le provisioning.

01_BlocN_iPhone.indd 166

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Faites Submit et vous devriez maintenant pouvoir tlcharger ce nouveau provisioning, comme la
Figure 21.6. Tlchargez-le pour un usage futur dans cette fiche.
Figure 21.6 :
Tlchargez
le nouveau
provisioning.

CRATION DUN

STORE MANAGER

Dans cette partie, nous allons crer une classe qui servira dinterface entre lApp Store et liPhone.
Cette classe permettra de demander lApp Store les lments disponibles lachat via lIn App
Purchase, procdera aux paiements, et sauvegardera le statut des achats. Lavantage, cest quune fois
quelle sera faite, vous pourrez la rutiliser dans toutes vos applications intgrant lIn App Purchase en
ne ralisant que de petites modifications.
Pour ce faire, nous allons crer une classe Singleton. Cest ce que lon appelle un design pattern au
mme titre que le MVC (Model View Controller) ou le delegate. Un design pattern (ou patron de
conception en franais), cest un schma de programmation mis en place pour simplifier la vie des
dveloppeurs. Lorsque lon parle de classe Singleton, on sous-entend une classe A qui contient un
objet de la classe A directement instanci. Ainsi, quand on se sert de la classe, on fait toujours appel
au mme objet qui nest instanci quune seule fois dans la classe.

Info
Il est trs important de commencer par lire la Fiche 11 concernant le patron de conception delegate
que nous exploitons ici. De plus, vous aurez besoin de la Fiche 35 traitant du protocole NSCoding
pour larchivage de donnes.
Commencez par crer un nouveau projet de type Window-based Application que vous nommerez
TestInAppPurchase. Ajoutez un nouveau fichier, Objective-C class, sous-classe de NSObject que
vous nommerez InAppPurchaseStoreManager. Incluez dans votre projet le framework StoreKit et
importez-le dans InAppPurchaseStoreManager.h.
Dans ce fichier, nous allons crer un protocole nomm InAppPurchaseStoreManagerDelegate

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
@protocol InAppPurchaseStoreManagerDelegate;
@interface InAppPurchaseStoreManager : NSObject {
id<InAppPurchaseStoreManagerDelegate> delegate;
}

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 167

167

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

168

LIn App Purchase (achat intgr)

@property (nonatomic, assign) id<InAppPurchaseStoreManagerDelegate> delegate;


@end
@protocol InAppPurchaseStoreManagerDelegate <NSObject>
@optional
// informe le delegate de lachat dun produit
- (void)productPurchased:(NSString *)productId;
@required
// demande au delegate dafcher la liste des produits
- (void)inAppPurchaseStoreManager:(InAppPurchaseStoreManager*)manager
askToDisplayListOfProducts:(NSMutableArray*)list;
@end
Ensuite, nous allons crer une mthode de classe, sharedManager, qui va instancier lobjet de classe.
Ajoutez dans le .m :

#import InAppPurchaseStoreManager.h
@implementation InAppPurchaseStoreManager
@synthesize delegate;
// notre manager
static InAppPurchaseStoreManager *sharedManager;
+ (InAppPurchaseStoreManager *)sharedManager
{
@synchronized(self)
{
if (!sharedManager)
sharedManager = [[InAppPurchaseStoreManager alloc] init];
return sharedManager;
}
return sharedManager;
}
- (void) dealloc {
[sharedManager release];
[super dealloc];
}
#pragma mark Singleton Methods
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self) {
if (sharedManager == nil) {
sharedManager = [super allocWithZone:zone];
return sharedManager;
}

01_BlocN_iPhone.indd 168

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; // sous-entend que lobjet ne peux pas tre release
}
- (void)release
{
// on ne fait rien !
}
- (id)autorelease
{
return self;
}
@end
Noubliez pas de dclarer la mthode dans le .h :

@property (nonatomic, assign) id<InAppPurchaseStoreManagerDelegate> delegate;

+ (InAppPurchaseStoreManager *)sharedManager;
@end

Info
Pour accder notre objet, il faudra donc faire InAppPurchaseStoreManager *manager =
[InAppPurchaseStoreManager sharedManager]; qui se chargera dinstancier un objet la premire
fois, puis renverra ensuite une rfrence vers lobjet sharedManager dclar en static. Si vous tes
dbutant, cela peut vous paratre difficile mais cela viendra par la suite.
Noublions pas que cette classe doit communiquer avec lApp Store et procder des paiements.
Chaque objet disponible via lIn App Purchase aura un identifiant que vous aurez choisi (nous le
verrons la section Ajouter des produits acheter). Ensuite, nous allons crer un tableau qui sauvegardera les objets avec un boolen associ prcisant si lobjet a dj t achet ou non.
Cela peut vous paratre inutile, car chaque objet a dj un type (achat pouvant tre rpt ou unique).

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 169

169

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

170

LIn App Purchase (achat intgr)

En fait, lorsque vous allez racheter un objet de type unique, une alert view prcisera automatiquement
que vous avez dj achet cet article, et que vous pourrez le tlcharger nouveau gratuitement. Mais
dans le cas o vous proposez un abonnement, il est prfrable de savoir au lancement de lapplication
si lutilisateur y a dj souscrit. Ainsi, il suffira de lire ce tableau pour le savoir !
La premire tape est de crer un objet IAPProduct respectant le protocole NSCoding pour larchiver
dans un fichier. Ajoutez un nouveau fichier de type Objective-C class sous-classe de NSObject dans
votre projet et nommez-le IAPProduct. Ensuite, crivez ces quelques lignes :

IAPProduct.h
#import <Foundation/Foundation.h>
#dene kIdentier @identier
#dene kPurchased @purchased
@interface IAPProduct : NSObject <NSCoding> {
NSString *productID;
BOOL purchased;
}
@property (nonatomic, retain) NSString *productID;
@property (assign) BOOL purchased;
@end
IAPProduct.m
#import IAPProduct.h
@implementation IAPProduct
@synthesize productID, purchased;
- (id) initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
self.productID = [coder decodeObjectForKey:kIdentier];
self.purchased = [coder decodeBoolForKey:kPurchased];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:productID forKey:kIdentier];
[coder encodeBool:purchased forKey:kPurchased];
}
- (void) dealloc {
[super dealloc];
[productID release];
}
@end
Dclarez le tableau qui nous servira stocker tous les objets de la classe IAPProduct, dans le .h de
InAppPurchaseStoreManager :

01_BlocN_iPhone.indd 170

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSMutableArray *listOfProductsPurchasedOrNot;
Puis crez la mthode pour lire le tableau depuis les prfrences dans le .m :
- (void) retrieveListProductsFromSavedFile {
if (listOfProductsPurchasedOrNot) {
[listOfProductsPurchasedOrNot release];
}
// on rcupre depuis le chier avec NSCoding
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory
stringByAppendingPathComponent:@products.myArchive];
NSData *data;
NSKeyedUnarchiver *unarchiver;
data = [[NSData alloc] initWithContentsOfFile:path];
if(data)
{
// si le chier contient des donnes, on dsarchive
unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
NSMutableArray *arrayArchived = [unarchiver decodeObjectForKey:@Products];
listOfProductsPurchasedOrNot = [arrayArchived retain];
[unarchiver nishDecoding];
[unarchiver release];
[data release];
}
if (!listOfProductsPurchasedOrNot) {
// le chier nexiste pas, on le cre
listOfProductsPurchasedOrNot = [[NSMutableArray array] retain];
// ajout du premier produit
IAPProduct *product1 = [[IAPProduct alloc] init];
product1.productID = kIdProduit1;
product1.purchased = NO;
[listOfProductsPurchasedOrNot addObject:product1];
[product1 release];
// ajout du second produit
IAPProduct *product2 = [[IAPProduct alloc] init];
product2.productID = kIdProduit2;
product2.purchased = NO;
[listOfProductsPurchasedOrNot addObject:product2];
[product2 release];
}

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 171

171

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

172

LIn App Purchase (achat intgr)

}
Noubliez pas limport dans le .h :

#import IAPProduct.h
Vous voyez ici que nous avons dfini des cls. Ajoutez donc ces lignes dans . h
// Liste des id
#dene kIdProduit1 @com.mondomaine.testInAppPurchase.001
#dene kIdProduit2 @com.mondomaine.testInAppPurchase.002
Chaque produit disponible via lIn App Purchase aura son propre identifiant quil vous conviendra de
dfinir. Un conseil : respectez com(ou fr).votreNomAppStore.nomApplication.numro, ce sera plus
simple pour vous y retrouver !

Info
Pour viter une mise jour lorsque vous ajoutez un produit disponible en achat intgr, rcuprez
cette liste depuis un webservice au lieu de le dfinir en dur comme nous le faisons ici. Ensuite, bien
entendu, il faut que votre application puisse grer ce nouveau contenu !
Nous ne souhaitons pas que cette mthode soit publique. Ainsi, nous allons crer une catgorie
(encore un patron de conception !). Pour cela, rajoutez ces lignes en gras dans le .m :

#import InAppPurchaseStoreManager.h

@interface InAppPurchaseStoreManager(private)
- (void) retrieveListProductsFromSavedFile;
@end
@implementation InAppPurchaseStoreManager
// ...
Nous allons appeler cette dernire mthode lors de linitialisation du manager :

+ (InAppPurchaseStoreManager *)sharedManager
{
@synchronized(self)
{
if (!sharedManager)
{
sharedManager = [[InAppPurchaseStoreManager alloc] init];
// on rcupre la liste des produits stocks dans un chier ou les
prfrences
[sharedManager retrieveListProductsFromSavedFile];
}
return sharedManager;
}
return sharedManager;
}

01_BlocN_iPhone.indd 172

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

La prochaine tape est de demander lApp Store les produits disponibles lachat pour notre application.
Ajoutez dans le .m cette mthode :

// demande la liste des produits disponibles auprs de lapp Store


- (void) requestProductData
{
// cre un tableau ne contenant que les identiers disponibles
// remarquez que la cl est le nom de lobjet NSString *productID de IAPProduct
NSArray *arrayOfIdentiers = [listOfProductsPurchasedOrNot
valueForKey:@productID];
// cration de la requte
SKProductsRequest *request= [[SKProductsRequest alloc]
initWithProductIdentiers: [NSSet setWithArray:arrayOfIdentiers]];
// self est delegate de SKProductsRequest
request.delegate = self; 
// on dmarre
[request start];
// il ny a pas de fuites, on release lobjet request plus tard
}
Nous souhaitons que cette mthode soit publique. Il faut donc la dclarer dans le .h :

- (void) requestProductData;
Ligne , on met self comme delegate de request. Ainsi, il faut ajouter dans le .m :

@interface InAppPurchaseStoreManager : NSObject <SKProductsRequestDelegate>


Cela implique galement dimplmenter correctement le protocole SKProductsRequestDelegate.
Ajoutez cette mthode dans le .m :

#pragma mark #pragma mark SKProductsRequestDelegate methods


// appele lorsque le delegate reoit la rponse de lApp Store
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
if (!listOfProductsAvailabled) {
listOfProductsAvailabled = [[NSMutableArray alloc] init];
}
// on enlve tous les objets
[listOfProductsAvailabled removeAllObjects];
[listOfProductsAvailabled addObjectsFromArray:response.products];
// vous dafcher une vue pour prsenter les donnes
[delegate inAppPurchaseStoreManager:self
askToDisplayListOfProducts:listOfProductsAvailabled];

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 173

173

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

174

LIn App Purchase (achat intgr)

// on release ici la requte instancie plus haut dans requestProductData


[request autorelease];
}
Noubliez pas la dclaration dans le .h de

NSMutableArray *listOfProductsAvailabled;
Cette mthode sera appele lorsque le delegate (ici sharedManager) recevra la rponse de lApp
Store. Il vous incombe ensuite de prsenter les donnes comme vous le souhaitez ! Nous les afficherons par la suite.

GRER

LE PAIEMENT

Imaginez que votre application prsente la liste des produits disponibles lachat. Lorsque le client en
slectionnera un, il faudra quil puisse lacheter. Pour cela, il faut solliciter SKPayment.
Ajoutez le protocole SKPaymentTransactionObserver dans le .h :

@interface InAppPurchaseStoreManager : NSObject <SKProductsRequestDelegate,


SKPaymentTransactionObserver>
En fait, la gestion du paiement se fait de manire compltement transparente pour vous grce un
observateur. Ainsi, si lutilisateur quitte lapplication alors quune transaction est en cours, elle sera
automatiquement termine au prochain lancement.
La premire ligne crire consiste en lajout dun observateur du statut des paiements :
+ (InAppPurchaseStoreManager *)sharedManager
{
@synchronized(self)
{
if (!sharedManager)
{
sharedManager = [[InAppPurchaseStoreManager alloc] init];
// on rcupre la liste des produits stocks dans un chier
[sharedManager retrieveListProductsFromSavedFile];

// on ajoute un observateur du store le plus tt possible pour grer


les achats
[[SKPaymentQueue defaultQueue] addTransactionObserver:sharedManager];
}
return sharedManager;
}
return sharedManager;
}

Pour demander le paiement, on crit une mthode publique ( dclarer dans le .h donc) :

#pragma mark #pragma mark buy methods

01_BlocN_iPhone.indd 174

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

- (void) wantToPurchaseProduct:(NSString*) productID {


if ([SKPaymentQueue canMakePayments]) 
{
SKPayment *payment = [SKPayment paymentWithProductIdentier:productID]; 
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
else
{
// Lutilisateur a dsactiv la possibilit dacheter -> prsenter une
alert si besoin
}
}
Ligne , il faut absolument appeler la mthode canMakePayments de la classe SKPaymentQueue car
lutilisateur peut avoir dsactiv lachat intgr. Ensuite, ligne , on cre un nouveau paiement que
lon ajoutera ensuite dans un buffer qui grera les paiements.

Info
Si vous proposez lutilisateur dacheter plusieurs objets, il suffit dcrire payment.quantity =

unNombre;
Ensuite, il faut implmenter correctement le protocole SKPaymentTransactionObserver pour
connatre le statut dun achat :

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)


transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
}
}

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 175

175

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

176

LIn App Purchase (achat intgr)

Voici la signification de ces trois tats :

SKPaymentTransactionStatePurchased LApp Store a correctement ralis le paiement. Votre


application doit donc montrer lutilisateur limpact de son achat.

SKPaymentTransactionStateFailed La transaction sest mal droule. Vous pouvez regarder la


proprit error pour en savoir plus.

SKPaymentTransactionStateRestored La transaction a restaur un contenu initialement achet


par lutilisateur. Vous pouvez lire la proprit originalTransaction pour connatre les informations de lachat original.
Pour finir avec ce manager, nous allons implmenter les mthodes completeTransaction:, failedTransaction: et restoreTransaction: qui seront prives, ainsi que recordTransaction: :
@interface InAppPurchaseStoreManager(private)
- (void) retrieveListProductsFromSavedFile;
- (void) completeTransaction: (SKPaymentTransaction *)transaction;
- (void) restoreTransaction: (SKPaymentTransaction *)transaction;
- (void) failedTransaction: (SKPaymentTransaction *)transaction;
- (void) recordTransaction:(SKPaymentTransaction*) transaction;
@end
Commencez par completeTransaction: :

- (void) completeTransaction: (SKPaymentTransaction *)transaction


{
// on enregistre
[self recordTransaction: transaction];
// on informe le delegate
if([delegate respondsToSelector:@selector(productPurchased:)])
{
[delegate productPurchased:transaction.payment.productIdentier];
}
// on nit la transaction
[[SKPaymentQueue defaultQueue] nishTransaction: transaction];
}
Il y a trois temps : la sauvegarde dans le fichier, puis on informe le delegate, sil implmente la mthode
productPurchased:, et on finit la transaction.
De mme,

- (void) restoreTransaction: (SKPaymentTransaction *)transaction


{
// on enregistre
[self recordTransaction: transaction];
// on informe le delegate
if([delegate respondsToSelector:@selector(productPurchased:)])
{
[delegate productPurchased:transaction.payment.productIdentier];

01_BlocN_iPhone.indd 176

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
// on nit la transaction
[[SKPaymentQueue defaultQueue] nishTransaction: transaction];
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// il y a eu une erreur dans lachat
}
// on nit la transaction
[[SKPaymentQueue defaultQueue] nishTransaction: transaction];
}
Et la mthode de sauvegarde (archivage avec NSCoding) :

- (void) recordTransaction:(SKPaymentTransaction*) transaction


{
// on numre pour mettre YES le boolen du produit achet
for (IAPProduct *iapProduct in listOfProductsPurchasedOrNot) {
if ([iapProduct.productID isEqualToString:transaction.payment.productIdentier]) {
iapProduct.purchased = YES;
}
}
// on sauvegarde en avec NSCoding
NSMutableData *data;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *archivePath =
[documentsDirectory stringByAppendingPathComponent:@products.myArchive];
NSKeyedArchiver *archiver;
data = [NSMutableData data];
archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:listOfProductsPurchasedOrNot forKey:@Products];
[archiver nishEncoding];
[data writeToFile:archivePath atomically:YES];
[archiver release];
}
Pour le plaisir, ajoutez une mthode publique qui permettra de savoir si lon a dj achet ou non un
produit :

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 177

177

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

178

LIn App Purchase (achat intgr)

// retourne un boolen selon si le produit a dj t achet ou non


- (BOOL) havePurchasedProduct:(NSString*) productID {
for(IAPProduct *iapProduct in listOfProductsPurchasedOrNot) {
if ([iapProduct.productID isEqualToString:productID]) {
return iapProduct.purchased;
}
}
}

A JOUTER

DES PRODUITS ACHETER

Arrtons un peu de coder quelques instants, et revenez votre navigateur prfr pour vous rendre
sur liTunes Connect : https://itunesconnect.apple.com/. Loguez-vous, puis rendez-vous dans Manage
Your Applications. Cliquez ensuite sur Add New Application et remplissez toutes les donnes ncessaires. Lorsque lon vous le demande, cochez Upload your binary later.
De retour sur la page daccueil de liTunes Connect, cliquez sur Manage Your In App Purchases puis
sur Create New. Slectionnez lapplication que vous venez dajouter puis choisissez le bon bundle ID
comme la Figure 21.7.

Figure 21.7 : Choisissez


le Bundle ID.

Ensuite, crez un nouveau produit en remplissant les informations comme aux Figures 21.8 et 21.9.
Noubliez pas quil faut bien remplir le Product ID comme nous lavons fait dans lapplication (ou
linverse : modifiez votre application en fonction des Product ID crs).

01_BlocN_iPhone.indd 178

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 21.8 : Choix


du nom, de l ID
et du prix.

Figure 21.9 : Choix du


nom affich et de la
description.

Le type choisi doit tre rflchi : si vous slectionnez Consumable ou Subscription, le produit pourra
tre achet plusieurs fois. Par contre, si vous choisissez Non-Consumable, le produit ne pourra tre
achet quune unique fois par lutilisateur et tous ses achats futurs seront gratuits.
Crez un deuxime produit comme la Figure 21.10.

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 179

179

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

180

LIn App Purchase (achat intgr)

Figure 21.10 :
Deuxime produit.

De retour la liste des produits sur iTunes Connect pour votre application, vous pouvez voir que le
statut est Pending Developer Approval. Il faut donc approuver ce que vous venez de faire en cliquant
sur Achat dune voiture de course puis sur Approve comme la Figure 21.11. Faites de mme pour
Le pack de 100 vies.
Vous pouvez crer plusieurs descriptions selon les langues des pays dans lesquels vous souhaitez
distribuer lapplication.

Figure 21.11 :
Proposez lachat dune
voiture de course.

01_BlocN_iPhone.indd 180

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

CRER

UN COMPTE DE TEST

Vous navez certainement pas envie de payer lorsque vous testez... Ainsi, il faut crer un nouveau
compte iTunes. Retournez laccueil diTunes Connect puis sur Manage Users et In App Purchase Test
User. Cliquez sur Add New User et renseignez les champs demands. Ladresse e-mail devrait tre
diffrente de celle de votre compte iTunes normal.
Il faut maintenant changer le compte principal de votre appareil :
1. Allez dans lapplication Rglages puis Store.
2. Dconnectez-vous puis reconnectez-vous avec lidentifiant que vous venez de crer.
3. Remplissez les informations demandes. Ne vous inquitez pas quand vous devez donner les
coordonnes de votre carte bancaire. En effet, vous pouvez vrifier que votre adresse est celle
dApple ! Tout va bien donc...

DERNIRE

LIGNE DROITE

Nous allons, pour finir, revenir notre projet et compiler notre application pour acheter des produits.
Commencez par glisser le provisioning cr au dbut de la fiche sur licne dXcode. Ensuite, modifiez
le .plist de lapplication et remplacez la valeur pour la cl Bundle Identifier par com.mondomaine.testInAppPurchase. (Le mme que celui spcifi lors de la cration du provisioning).
Puis, ajoutez un nouveau fichier dans votre projet, de type UIViewController sous-classe de UITableViewController que vous nommerez StoreDisplayTableViewController. Ce contrleur servira afficher les diffrents produits que lutilisateur pourra acheter. Crez galement un fichier de type

UIViewController que vous nommerez RootViewController. Laissez cocher With XIB for User Interface pour ces deux fichiers.
Instanciez ce contrleur dans lapplication delegate :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
rootViewController = [[RootViewController alloc]
initWithNibName:@RootViewController bundle:nil];
[rootViewController.view setFrame:[[UIScreen mainScreen] applicationFrame]];
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
Noubliez pas que le .h doit tre :

#import <UIKit/UIKit.h>
#import RootViewController.h
@interface TestInAppPurchaseAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 181

181

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

182

LIn App Purchase (achat intgr)

RootViewController *rootViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
Ensuite, ajoutez un bouton la vue du RootViewController pour afficher le store (dans
RootViewController.m) :

- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
UIButton *buttonStore = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonStore addTarget:self action:@selector(askForListOfProducts:)
forControlEvents:UIControlEventTouchUpInside];
[buttonStore setTitle:@Store forState:UIControlStateNormal];
[buttonStore setFrame:CGRectMake(30, 30, 100, 30)];
[self.view addSubview:buttonStore];
[InAppPurchaseStoreManager sharedManager].delegate = self;
}
return self;
}
Dans le .h, faire un import du manager puis dclarer RootViewController comme tant delegate de
InAppPurchaseStoreManagerDelegate.

#import <UIKit/UIKit.h>
#import InAppPurchaseStoreManager.h
@interface RootViewController : UIViewController <InAppPurchaseStoreManagerDelegate> {
}
@end
Lorsque lon appuiera sur le bouton Store, on demandera la liste des produits lApp Store :

- (void) askForListOfProducts:(id)sender {
// demande lApp Store la liste des produits
[[InAppPurchaseStoreManager sharedManager] requestProductData];
}
Ensuite, il faut implmenter le protocole InAppPurchaseStoreManagerDelegate en commenant par
la mthode appele lorsque le manager a termin de tlcharger la liste des produits. cet endroit,
on affichera le table view controller cr juste avant :

#pragma mark #pragma mark InAppPurchaseStoreManager delegate


- (void) inAppPurchaseStoreManager:(InAppPurchaseStoreManager *)manager
askToDisplayListOfProducts:(NSMutableArray *)list {
// on rcupre le tableau

01_BlocN_iPhone.indd 182

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

self.arrayToDisplay = list;
// si le navigation controller nexiste pas, on le cre
if(!navController)
{
StoreDisplayTableViewController
*storeDisplayViewController =
[[StoreDisplayTableViewController alloc] initWithStyle:UITableViewStylePlain];
navController = [[UINavigationController alloc]
initWithRootViewController:storeDisplayViewController];
storeDisplayViewController.rootViewController = self; 
[storeDisplayViewController release];
}
// on afche la vue
[self presentModalViewController:navController animated:YES];
}

Info
On utilise un contrleur de navigation pour afficher correctement la table view. De plus, le contrleur
de la classe StoreDisplayTableViewController aura besoin dun tableau de donnes afficher. Pour
cela, il devra rcuprer le tableau arrayToDisplay de RootViewController. Il faut donc que linstance
storeDisplayViewController connaisse linstance actuelle de RootViewController, do la ligne .
Il faut donc modifier le .h :

#import <UIKit/UIKit.h>
#import InAppPurchaseStoreManager.h
@interface RootViewController : UIViewController <InAppPurchaseStoreManagerDelegate> {
NSMutableArray *arrayToDisplay;
UINavigationController *navController;
}
@property (nonatomic, retain) NSMutableArray *arrayToDisplay;
@end
Et ajouter dans le .m :
@synthesize arrayToDisplay;
Ajouter galement la mthode appele lorsque lutilisateur aura achet un article :

- (void) productPurchased:(NSString *)productId {


NSLog(@Vous venez dacheter %@, productId);
}
Ne pas oublier le dealloc :

- (void)dealloc {
[arrayToDisplay release];
[navController release];
[super dealloc];
}

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 183

183

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

184

LIn App Purchase (achat intgr)

Ni limport dans RootViewController.m de StoreDisplayTableViewController.h :

#import StoreDisplayTableViewController.h
Pour afficher les donnes, nous avons besoin dune table view.Voici le StoreDisplayTableViewControl
ler.h :
#import <UIKit/UIKit.h>
#import RootViewController.h
@class RootViewController;
@interface StoreDisplayTableViewController : UITableViewController {
RootViewController *rootViewController;
}
@property (nonatomic, retain) RootViewController *rootViewController;
@end
Noubliez pas cette ligne dans le .m :

@synthesize rootViewController;
Ajoutez un bouton dans la barre de navigation pour le retour (dans le .m) :

- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *buttonBack = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self
action:@selector(backToRoot:)];

self.navigationItem.leftBarButtonItem = buttonBack;
[buttonBack release];
self.title = @Liste des achats;
}
- (void) backToRoot:(id)sender {
[rootViewController dismissModalViewControllerAnimated:YES];
}
Puis, spcifiez le nombre de sections et de lignes :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [rootViewController.arrayToDisplay count];
}

01_BlocN_iPhone.indd 184

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Dans la mthode cellForRowAtIndexPath:, affichez les donnes :

- (UITableViewCell *)tableView:(UITableView *)tableView


cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentier:CellIdentier];

if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
// on rcupre le produit de la ligne
SKProduct *product = [rootViewController.arrayToDisplay
objectAtIndex:indexPath.row];
// formate le chiffre selon le pays
NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init]
autorelease];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedPrice = [numberFormatter stringFromNumber:product.price];
//NSLog(@Produit : %@\nDescription : %@ \nPrix : %@, ID : %@,[product
localizedTitle], [product localizedDescription], formattedPrice,
[product productIdentier]);
cell.textLabel.text = [NSString stringWithFormat:@%@ %@,
[product localizedTitle], formattedPrice];
return cell;
}

Info
Notez que toutes les donnes sont localises ! Rfrez-vous la Fiche 31.
Pour finir, lorsque nous cliquons sur un produit, lancer lachat !

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath


*)indexPath {
// on rcupre le produit acheter
SKProduct *productToPurchase = [rootViewController.arrayToDisplay

21

LIn App Purchase (achat intgr)

01_BlocN_iPhone.indd 185

185

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

21

LIn App Purchase (achat intgr)

186

objectAtIndex:indexPath.row];
// on demande au manager dacheter lobjet
[[InAppPurchaseStoreManager sharedManager]
wantToPurchaseProduct:productToPurchase.productIdentier];

}
Noubliez pas le dealloc :

- (void)dealloc {
[rootViewController release];
[super dealloc];
}

01_BlocN_iPhone.indd 186

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

C HAPITRE 5
ALLER PLUS LOIN

01_BlocN_iPhone.indd 187

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Aprs avoir manipul des lments simples dinterface et commenc


apprhender la programmation iPhone, la prochaine tape est la manipulation
de plusieurs vues et les manires de les prsenter. Ensuite, vous apprendrez
crer des animations et communiquer avec un site web pour finir par inclure
de la publicit dans votre application.

01_BlocN_iPhone.indd 188

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

Comprendre la gestion des vues


22 Comprendre la gestion des vues

Sur liPhone, contrairement aux ordinateurs, vous navez quune seule fentre de disponible pour votre application. Ainsi, laffichage se fera selon un empilement des vues. tant
dbutant, il est assez difficile de le visualiser. Faisons-le ensemble !
Dans cette fiche, nous allons trs simplement manipuler deux vues, que lon enlvera/affichera grce
un bouton. Ensuite, nous verrons comment implmenter une gestion plus complique, en utilisant
un RootViewController.
Tout dabord crez un nouveau projet de type Window-based Application que vous nommerez
GestionVues. Ajoutez ensuite un fichier de type UIViewController que vous nommerez RootViewController. Enfin, ajoutez deux fichiers de type UIView, comme la Figure 22.1, que vous nommerez
respectivement Vue1 et Vue2.

Figure 22.1 : Ajoutez un


fichier de type UIView.

Dans un premier temps, nous allons ajouter la vue du RootViewController la fentre, dans lapplication delegate.

GestionVuesAppDelegate.h
#import <UIKit/UIKit.h>
#import RootViewController.h
@class RootViewController; 
@interface GestionVuesAppDelegate : NSObject <UIApplicationDelegate> {

22

Comprendre la gestion des vues

01_BlocN_iPhone.indd 189

189

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

190

Comprendre la gestion des vues

RootViewController *rootViewController;
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) RootViewController *rootViewController; 
@end

GestionVuesAppDelegate.m
#import GestionVuesAppDelegate.h
@implementation GestionVuesAppDelegate
@synthesize window;
@synthesize rootViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
RootViewController *viewController = [[RootViewController alloc] init];
self.rootViewController = viewController;
[viewController release];
[window addSubview:rootViewController.view]; 
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[rootViewController release];
[window release];
[super dealloc];
}
@end
Rien de nouveau dans ce code, si ce nest une petite diffrence avec lobjet rootViewController
instanci. En effet, ligne , on se sert du mcanisme @property et @synthesize qui, rappelez-vous,
remplace les getters/setters.
Ligne , nous utilisons le mot-cl @class. Cela permet de spcifier au compilateur que tout usage de
RootViewController en tant que nom de classe, dans la dclaration de variables lintrieur dune
autre classe (ici GestionVuesAppDelegate) est autoris.
Ligne , on ajoute la vue du RootViewController la pile de vues une fois que tout saffichera. La pile
de vues sera donc comme reprsente la Figure 22.2.

01_BlocN_iPhone.indd 190

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

rootViewController.view

Figure 22.2 : Piles de vues,


avec le rootViewController.
Window

Ensuite, dans le RootViewController, nous allons ajouter la vue1.

RootViewController.h
#import <UIKit/UIKit.h>
#import Vue1.h
@class Vue1;
@interface RootViewController : UIViewController {
Vue1 *vue1;
}
@end
Modifiez le loadView :

RootViewController.m
- (void) loadView {
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]] autorelease]; 
self.view.backgroundColor = [UIColor whiteColor];
vue1 = [[Vue1 alloc] initWithFrame:self.view.frame];
[self.view addSubview:vue1];
}

Info
Il est important de noter que lorsque lon nutilise pas de fichier .xib, la vue du view controller nest pas
alloue. Il faut donc le faire manuellement ligne . Cette mthode sera appele automatiquement si
view pointe vers nil. Si vous lancez un fichier .xib mais que vous crez tout de mme votre interface
la main, il faudra implmenter initWithNibName: bundle: voire viewDidLoad.
Ensuite, ajoutons dans la vue 1 un bouton qui permettra de changer de vue, ainsi quun label pour
afficher le nom de la vue.

Vue1.h
#import <UIKit/UIKit.h>
#import GestionVuesAppDelegate.h

22

Comprendre la gestion des vues

01_BlocN_iPhone.indd 191

191

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

192

Comprendre la gestion des vues

@interface Vue1 : UIView {


UILabel *labelStatut;
}
@end

Vue1.m
#import Vue1.h
@implementation Vue1
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:CGRectMake(0, 0, 160, 30)];
[button setCenter:self.center];
[button addTarget:self action:@selector(allerALaVue2:)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@Aller la vue 2 forState:UIControlStateNormal];
[self addSubview:button];
labelStatut = [[UILabel alloc] initWithFrame:CGRectMake(30, 30, 260, 30)];
labelStatut.text = @On est sur la vue 1;
labelStatut.textColor = [UIColor blueColor];
[self addSubview:labelStatut];
}
return self;
}
- (void) allerALaVue2:(id)sender {
GestionVuesAppDelegate *appDelegate =
(GestionVuesAppDelegate*)[[UIApplication sharedApplication] delegate]; 
[appDelegate.rootViewController changePourLaVue2]; 
}
- (void)dealloc {
[labelStatut release];
[super dealloc];
}
@end
Nous allons nous servir du rootViewController pour changer nos vues, vue1 et vue2. Pour accder
lobjet rootViewController instanci par lapplication delegate, on fait tout dabord une rfrence
vers le delegate de lapplication ligne . Ensuite, on appelle la mthode changePourLaVue2 du
rootViewController ligne .
Faisons le point sur notre pile de vues : par-dessus la vue du rootViewController, nous affichons la
vue1, qui contiendra un bouton et un label.Vous trouverez un schma Figure 22.3 rsumant tout ceci.

01_BlocN_iPhone.indd 192

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

LabelStatut
Boutton
Vue 1

rootViewController.view

Figure 22.3 :
Piles de vues, avec la vue 1.

Window

Implmentez Vue2.h et Vue2.m de la mme manire. Seules ces lignes changeront :

[button addTarget:self action:@selector(allerALaVue1:)


forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@Aller la vue 1 forState:UIControlStateNormal];
// ainsi que
labelStatut.text = @On est sur la vue 2;
labelStatut.textColor = [UIColor redColor];
// et
- (void) allerALaVue1:(id)sender {
GestionVuesAppDelegate *appDelegate =
(GestionVuesAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate.rootViewController changePourLaVue1];
}
Il ne reste plus qu ajouter ou enlever les bonnes vues, et ceci dans RootViewController.m :

- (void) changePourLaVue1 {
[vue2 removeFromSuperview]; 
[self.view addSubview:vue1]; 
}
- (void) changePourLaVue2 {
if(!vue2)
vue2 = [[Vue2 alloc] initWithFrame:self.view.frame]; 
[vue1 removeFromSuperview];
[self.view addSubview:vue2];
}
Cest lappel removeFromSuperview (enlever vue2 de la vue sur laquelle elle repose : rootViewController.view) qui va enlever vue2 ligne . Ensuite, on ajoute ligne  la vue1 la vue du rootViewController grce la mthode addSubview:.

22

Comprendre la gestion des vues

01_BlocN_iPhone.indd 193

193

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

194

Comprendre la gestion des vues

Cest ici quil peut y avoir une confusion. En effet, en enlevant la vue2, on enlve galement le bouton
et le label. Pourquoi ? Tout simplement parce que le bouton et le label sont ajouts la vue2. Ainsi, il
faut prciser que la notion de pile de vues comprend des vues avec des sous-vues potentielles.
Finalement, le label et le bouton appartiennent la vue2. En fait, pour chaque vue, on peut retrouver
ses sous-vues grce NSArray *sousVues = [maVue subviews];. De fait, un removeFromSuperview
enlve la vue et toutes ses sous-vues de la pile.
De mme, [maVue superview] renvoi la vue sur laquelle est pose maVue.

Info
Notez que lon peut galement ajouter une vue dans la pile grce insertSubview: atIndex:. Ainsi, on
peut choisir quelle position insrer la vue dans la pile. En effet, chaque vue dans la pile correspond
un index. Par exemple, dans vue1, le bouton est lindex 0, le label lindex 1 dans la sous-pile de vue1.
Vous pouvez galement dplacer une vue dans la pile, avec les mthodes suivantes :

bringSubviewToFront: Met la vue passe en paramtre au-dessus de la pile de vues.


sendSubviewToBack: Met la vue passe en paramtre en dessous de la pile de vues.
insertSubview:aboveSubview: Insre la vue passe en paramtre 1 au-dessus de la vue passe
en paramtre 2.

insertSubview:belowSubview: Insre la vue passe en paramtre 1 en dessous de la vue passe


en paramtre 2.

exchangeSubviewAtIndex:withSubviewAtIndex: change les vues en fonction de leur index.


Enfin, notez que ligne , on teste si la vue2 a dj t alloue ou non. Si ce nest pas le cas, on le fait.
La raison en est trs simple : par dfaut, on affiche la vue1. Mais rien ne garantit que lutilisateur va
afficher la vue2. Dans ce cas, pourquoi allouer de la mmoire pour un objet qui ne sera pas utilis ?
Cest une rgle dor que vous devrez respecter par la suite : viter tout chargement mmoire
inutile !

RLE DE LA FICHE ET SUITE


Cette fiche a un rle pdagogique. Cependant, vous serez probablement amen par la suite recourir
un rootViewController qui sera un objet de gestion des contrleurs au-dessus. Pour cela, imaginez ce
cas concret : une application avec un menu (MenuViewController), une vue de jeu (JeuViewController)
et une vue de paramtres (ParametresViewController). Pour naviguer facilement entre ses vues, une
solution consiste utiliser un RootViewController, qui servira changer les vues entre elles. Prenons
ce cas et ralisons-le.

Vous allez donc construire cette interface. Commencez par crer un projet Window-based Application que vous nommerez UtiliserUnRoot. Ajoutez ensuite quatre view controllers : RootViewController, JeuViewController, MenuViewController et ParametresViewController.

01_BlocN_iPhone.indd 194

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ajoutez la vue du rootViewController la window dans lapplication delegate, comme au dbut de


cette fiche. Essayez maintenant de construire le schma suivant :
1. Lorsque le rootViewController apparat, ajoutez la vue du menu.
2. Ajoutez dans la vue du menu view controller deux boutons qui serviront se rendre sur la vue
du jeu ou la vue des paramtres, ainsi quun label affichant le nom de la vue courante.
3. Dans les vues des deux contrleurs de Jeu et Parametres, ajoutez un bouton Retour au menu
ainsi quun label indiquant le nom de la vue courante.
Notez que ce sera le RootViewController qui possdera les trois contrleurs Jeu, Menu, et Parametres
Cest un trs bon exercice, qui vous permettra de vrifier vos acquis.
Voici ce que vous avez d construire (je vous laisse construire ParametresViewController sur le
modle de JeuViewController) :

RootViewController.h
#import <UIKit/UIKit.h>
#import MenuViewController.h
#import JeuViewController.h
#import ParametresViewController.h
@class MenuViewController;
@class JeuViewController;
@class ParametresViewController;
@interface RootViewController : UIViewController {
MenuViewController *menuViewController;
JeuViewController *jeuViewController;
ParametresViewController *parametresViewController;
}
- (void) changePourJeu;
- (void) changePourParametres;
@end

RootViewController.m
#import RootViewController.h
@implementation RootViewController
- (void) loadView {
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen]
applicationFrame]] autorelease];
self.view.backgroundColor = [UIColor whiteColor];
menuViewController = [[MenuViewController alloc] init];
[self.view addSubview:menuViewController.view];

22

Comprendre la gestion des vues

01_BlocN_iPhone.indd 195

195

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

196

Comprendre la gestion des vues

}
(void) changePourJeu {
}
- (void) changePourParametres {
}
- (void)dealloc {
[menuViewController release];
[jeuViewController release];
[parametresViewController release];
[super dealloc];
}
MenuViewController.h
#import <UIKit/UIKit.h>
#import UtiliserUnRootAppDelegate.h
@interface MenuViewController : UIViewController {
UILabel *labelStatut;
}
@end

MenuViewController.m
#import MenuViewController.h
@implementation MenuViewController
- (void) loadView {
self.view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)]
autorelease];
self.view.backgroundColor = [UIColor whiteColor];
UIButton *boutonJeu = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[boutonJeu setFrame:CGRectMake(20, 20, 160, 30)];
[boutonJeu addTarget:self action:@selector(changePourJeu:)
forControlEvents:UIControlEventTouchUpInside];
[boutonJeu setTitle:@Aller dans jeu forState:UIControlStateNormal];
[self.view addSubview:boutonJeu];
UIButton *boutonParametres = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[boutonParametres setFrame:CGRectMake(20, 60, 200, 30)];
[boutonParametres addTarget:self action:@selector(changePourParametres:)
forControlEvents:UIControlEventTouchUpInside];
[boutonParametres setTitle:@Aller dans les paramtres
forState:UIControlStateNormal];
[self.view addSubview:boutonParametres];

01_BlocN_iPhone.indd 196

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

labelStatut = [[UILabel alloc] initWithFrame:CGRectMake(30, 30, 260, 30)];


[labelStatut setCenter:self.view.center];
labelStatut.text = @On est sur le menu;
labelStatut.textColor = [UIColor blueColor];
[self.view addSubview:labelStatut];
}
- (void) changePourJeu : (id) sender {
UtiliserUnRootAppDelegate *appDelegate =
(UtiliserUnRootAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate.rootViewController changePourJeu];
}
- (void) changePourParametres : (id) sender {
UtiliserUnRootAppDelegate *appDelegate =
(UtiliserUnRootAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate.rootViewController changePourParametres];
}
- (void)dealloc {
[labelStatut release];
[super dealloc];
}
@end
JeuViewController.h
#import <UIKit/UIKit.h>
#import UtiliserUnRootAppDelegate.h
@interface JeuViewController : UIViewController {
UILabel *labelStatut;
}
@end
JeuViewController.m
#import JeuViewController.h
@implementation JeuViewController
- (void) loadView {
// attention la barre de statuts qui ne fait effet que dans la vue du
rootViewController, vu que self.view est une subview de rootViewController
self.view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)]
autorelease];
self.view.backgroundColor = [UIColor whiteColor];
UIButton *boutonMenu = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[boutonMenu setFrame:CGRectMake(20, 20, 160, 30)];

22

Comprendre la gestion des vues

01_BlocN_iPhone.indd 197

197

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

198

Comprendre la gestion des vues

[boutonMenu addTarget:self action:@selector(changePourMenu:)


forControlEvents:UIControlEventTouchUpInside];

[boutonMenu setTitle:@Retour au menu forState:UIControlStateNormal];


[self.view addSubview:boutonMenu];
labelStatut = [[UILabel alloc] initWithFrame:CGRectMake(30, 30, 260, 30)];
[labelStatut setCenter:self.view.center];
labelStatut.text = @On est sur le jeu;
labelStatut.textColor = [UIColor redColor];
[self.view addSubview:labelStatut];
}
- (void) changePourMenu : (id) sender {
UtiliserUnRootAppDelegate *appDelegate =
(UtiliserUnRootAppDelegate*)[[UIApplication sharedApplication] delegate];
// on appelle changePourJeu qui va nous permettre de revenir au menu
[appDelegate.rootViewController changePourJeu];
}
- (void)dealloc {
[labelStatut release];
[super dealloc];
}
@end
Il ne vous aura pas chapp que nous navons crit aucun code enlevant une vue pour en remettre
une autre. Faisons-le pour JeuViewController en implmentant la mthode changePourJeu dans
RootViewController.m. Dans cette mthode, nous allons regarder quelle vue du menu ou du jeu
saffiche. Ensuite, nous enlverons la vue affiche pour mettre lautre la place.Vous allez voir, cest un
peu subtil...

- (void) changePourJeu {
if (!jeuViewController) {
// rappelez-vous, on nalloue que si lon a besoin
jeuViewController = [[JeuViewController alloc] init];
}
// on fait rfrence sur les vues de menu et jeu
UIView *menuView = menuViewController.view;
UIView *jeuView = jeuViewController.view;
if([menuView superview] != nil)
// si menuView repose sur une vue, alors on lenlve 

{
[jeuViewController viewWillAppear:NO]; 
[menuViewController viewWillDisappear:NO];
[menuView removeFromSuperview];
[self.view addSubview:jeuView];

01_BlocN_iPhone.indd 198

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[jeuViewController viewDidAppear:NO];
[menuViewController viewDidDisappear:NO];
}
else // sinon, cest jeuView que lon enlve
{
[menuViewController viewWillAppear:NO];
[jeuViewController viewWillDisappear:NO];
[jeuView removeFromSuperview];
[self.view addSubview:menuView];
[menuViewController viewDidAppear:NO];
[jeuViewController viewDidDisappear:NO];
}
}
Pour savoir quelle vue enlever, et quelle vue remettre, on teste ligne  si la superview de menu ne
pointe pas vers nil (pointeur nul). En fait, cela revient tester si la vue du menu est dans la pile de
vue.
Ligne , on appelle explicitement les mthodes viewWillAppear:(BOOL)animated, viewWillDisappear:(BOOL)animated... On spcifie non pour lanimation car la transition se fera sans animations.
Compilez et lancez votre application pour tester. Faites de mme pour afficher la vue de paramtres.
Pour finir, nous allons ajouter une animation de type flipside (retournement de 180) laide de
mthodes de haut niveau de Core Animation. Modifiez la mthode ci-dessus pour obtenir :

- (void) changePourJeu {
if (!jeuViewController) {
// rappelez-vous, on nalloue que si lon a besoin
jeuViewController = [[JeuViewController alloc] init];
}
// on fait rfrence sur les vues de menu et jeu
UIView *menuView = menuViewController.view;
UIView *jeuView = jeuViewController.view;
[UIView beginAnimations:@ipAnimation context:nil]; // rinitialiser le layer
[UIView setAnimationDuration:0.5]; // dure de lanimation
if([menuView superview] != nil) // si menuView repose sur une vue, alors on lenlve
{
[jeuViewController viewWillAppear:YES];
[menuViewController viewWillDisappear:YES];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
forView:self.view cache:YES]; // type de lanimation
[menuView removeFromSuperview];
[self.view addSubview:jeuView];
[jeuViewController viewDidAppear:YES];
[menuViewController viewDidDisappear:YES];
}

22

Comprendre la gestion des vues

01_BlocN_iPhone.indd 199

199

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

22

200

Comprendre la gestion des vues

else // sinon, cest jeuView que lon enlve


{
[menuViewController viewWillAppear:YES];
[jeuViewController viewWillDisappear:YES];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
forView:self.view cache:YES];
[jeuView removeFromSuperview];
[self.view addSubview:menuView];
[menuViewController viewDidAppear:YES];
[jeuViewController viewDidDisappear:YES];
}
[UIView commitAnimations]; // lancer lanimation
}

Info
Lautre animation disponible est UIViewAnimationTransitionCurlUp ou UIViewAnimationTransitionCurlDown. Cette animation permet de simuler un retournement de page.

01_BlocN_iPhone.indd 200

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

23

Navigation entre vues


23 Navigation entre vues

la fiche prcdente, nous avons vu comment architecturer la navigation entre vues


autour dun RootViewController. Il existe dautres mthodes pour afficher vos vues.
Vous allez commencer par une mthode qui permet trs simplement de naviguer entre deux vues, ou
plusieurs lorsque la hirarchie reste simple. Cette mthode repose sur la notion de protocole et
delegate, il faut donc pralablement lire la Fiche 11. Dans un second temps, nous apprendrons nous
servir dun contrleur de navigation (UINavigationController) qui permet de mettre en place trs
rapidement une hirarchie de vues complique.
Typiquement, le choix dune mthode pour raliser votre navigation dpend :

De la complexit de la hirarchie des vues et des relations entre vues elles-mmes. En effet, si la
hirarchie se dveloppe progressivement, comme dans lapplication Rglages, un contrleur de
navigation est trs adapt. linverse, si les transitions entre vues se rsument changer la vue
principale avec une vue de rglages, alors la premire mthode de cette fiche conviendra amplement. Pour finir, si votre application prsente des relations entre vues (typiquement un jeu vido
avec la vue du jeu, du menu, des prfrences...) un RootViewController sera bien adapt.

Du contrle sur les animations des vues. Nous lavons vu, avec un RootViewController, vous
pouvez facilement personnaliser vos animations pour changer les vues (les mthodes dUIView
ou celles plus bas niveau de Core Animation avec les layers que nous verrons la Fiche 26). Les
deux autres mthodes quant elles proposent des animations prdfinies.

UTILISER

UN DELEGATE POUR NAVIGUER SIMPLEMENT

Pour commencer, nous allons raliser une navigation entre deux vues et on choisira parmi quatre
types danimations prdfinies. La Figure 23.1 reproduit le schma de ce que lon souhaite raliser.
Crez un nouveau projet de type Window-based Application que vous nommerez Navigation. Ajoutez
ensuite deux fichiers de type UIViewController et cochez With XIB for user interface pour viter de
crer la vue de chaque contrleur manuellement.Vous nommerez ces fichiers MainViewController et
SecondViewController.
Quallons-nous faire ? Dans un premier temps, nous allons modifier notre application delegate pour
afficher la vue du MainViewController. Ensuite, nous afficherons la vue du SecondViewController
avec la mthode dont la signature est presentModalViewController: animated:. Enfin, nous crerons
un protocole delegate dans SecondViewController pour informer le MainViewController que la vue
de SecondViewController souhaite se retirer.

23

Navigation entre vues

01_BlocN_iPhone.indd 201

201

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

23

202

Navigation entre vues

Figure 23.1 : Le but de


cette premire partie.

Commenons donc par notre application delegate comme notre habitude :

NavigationAppDelegate.h
#import <UIKit/UIKit.h>
@class MainViewController;
@interface NavigationAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MainViewController *mainViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
NavigationAppDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
mainViewController = [[MainViewController alloc]
initWithNibName:@MainViewController bundle:nil];

mainViewController.view.frame = [[UIScreen mainScreen] applicationFrame];


[window addSubview:mainViewController.view];
[window makeKeyAndVisible];
return YES;
}
Noubliez pas le dealloc !

01_BlocN_iPhone.indd 202

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Occupons-nous du protocole delegate de SecondViewController, trs simplement lui aussi, si vous


avez bien lu la Fiche 11 :

SecondViewController.h
#import <UIKit/UIKit.h>
@protocol SecondViewControllerDelegate;
@interface SecondViewController : UIViewController {
id<SecondViewControllerDelegate> delegate;
}
@property (nonatomic, assign) id<SecondViewControllerDelegate> delegate;
@end
@protocol SecondViewControllerDelegate
- (void)secondViewControllerDidFinish:(SecondViewController *)controller;
@end
SecondViewController.m
@synthesize delegate;
- (void)viewDidLoad {
// on met une couleur pour bien voir les animations
[self.view setBackgroundColor:[UIColor redColor]];
// construction du bouton retour
UIButton *theButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[theButton addTarget:self action:@selector(retourToMain:)
forControlEvents:UIControlEventTouchUpInside];
[theButton setFrame:CGRectMake(0, 0, 70, 30)];
[theButton setCenter:self.view.center];
[theButton setTitle:@Retour forState:UIControlStateNormal];
[self.view addSubview:theButton];
[super viewDidLoad];
}
- (void)retourToMain:(id)sender {
// informe le delegate que lon souhaite se retirer
[self.delegate secondViewControllerDidFinish:self];
}
Pour finir, nous allons nous occuper de MainViewController. Il faut tout dabord quil soit conforme
au protocole SecondViewControllerDelegate. Pour cela, dans le .h :

#import <UIKit/UIKit.h>
#import SecondViewController.h
@interface MainViewController : UIViewController <SecondViewControllerDelegate> {
}
@end

23

Navigation entre vues

01_BlocN_iPhone.indd 203

203

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

23

204

Navigation entre vues

Et ajoutez dans le .m :

// Mthode delegate de SecondViewController


- (void)secondViewControllerDidFinish:(SecondViewController *)controller {
}
Modifiez le viewDidLoad pour afficher une couleur bleue :

// on met une couleur pour bien voir les animations


[self.view setBackgroundColor:[UIColor blueColor]];
Pour vous montrer les possibilits des animations, nous allons utiliser les quatre. Il faudra donc crer
quatre boutons ! Les animations sont les suivantes :

UIModalTransitionStyleFlipHorizontal Cette animation permet une rotation de 180 suivant


un axe vertical. Elle est typiquement lance dans le lecteur de musique de votre appareil quand
vous passez dune pochette dalbum aux titres de lalbum.

UIModalTransitionStyleCoverVertical Anime larrive de la deuxime vue en la faisant glisser


par-dessus la premire en arrivant par le bas.

UIModalTransitionStyleCrossDissolve Cette animation cre un fondu.


UIModalTransitionStylePartialCurl Toute nouvelle, cette animation tait auparavant disponible
mais non documente. Elle permet de prsenter une vue comme dans lapplication Plans lorsque
lon choisit la vue satellite, carte ou mixte.
Modifiez le viewDidLoad pour ajouter les quatre boutons :

// ip
UIButton *ipButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[ipButton addTarget:self action:@selector(ipToSecondView:)
forControlEvents:UIControlEventTouchUpInside];
[ipButton setFrame:CGRectMake(30, 30, 120, 30)];
[ipButton setTitle:@Flip forState:UIControlStateNormal];
[self.view addSubview:ipButton];
// Recouvrement vertical
UIButton *coverButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[coverButton addTarget:self action:@selector(coverToSecondView:)
forControlEvents:UIControlEventTouchUpInside];
[coverButton setFrame:CGRectMake(170, 30, 120, 30)];
[coverButton setTitle:@Recouvrement forState:UIControlStateNormal];
[self.view addSubview:coverButton];
// Fondu
UIButton *dissolveButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[dissolveButton addTarget:self action:@selector(dissolveToSecondView:)
forControlEvents:UIControlEventTouchUpInside];
[dissolveButton setFrame:CGRectMake(30, 80, 120, 30)];
[dissolveButton setTitle:@Fondu forState:UIControlStateNormal];
[self.view addSubview:dissolveButton];

01_BlocN_iPhone.indd 204

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// Recouvrement partiel
UIButton *curlButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[curlButton addTarget:self action:@selector(partialCurlToSecondView:)
forControlEvents:UIControlEventTouchUpInside];
[curlButton setFrame:CGRectMake(170, 80, 120, 30)];
[curlButton setTitle:@Curl forState:UIControlStateNormal];
[self.view addSubview:curlButton];
Puis ajoutez ces mthodes :

- (void) ipToSecondView:(id)sender {
[self goToSecondView:UIModalTransitionStyleFlipHorizontal];
}
- (void) coverToSecondView:(id)sender {
[self goToSecondView:UIModalTransitionStyleCoverVertical];
}
- (void) dissolveToSecondView:(id)sender {
[self goToSecondView:UIModalTransitionStyleCrossDissolve];
}
- (void) partialCurlToSecondView:(id)sender {
[self goToSecondView:UIModalTransitionStylePartialCurl];
}
Pour afficher la vue, nous allons recourir la mthode de la classe UIViewController : presentModal
ViewController:animated:. Pour enlever ce contrleur, il faudra faire appel dismissModalViewControllerAnimated:. Ajoutez cette mthode (la dclarer galement dans le .h) :
- (void)goToSecondView:(UIModalTransitionStyle)transitionStyle {
// allocation dune instance de SecondViewController
SecondViewController *controller = [[SecondViewController
alloc] initWithNibName:@SecondViewController bundle:nil];
// self est le delegate
controller.delegate = self;
//on choisit lanimation selon le paramtre
controller.modalTransitionStyle = transitionStyle;
// on afche controller
[self presentModalViewController:controller animated:YES];
[controller release];
}
Et modifiez la mthode delegate dans MainViewController :

// Mthode delegate de SecondViewController


(void)secondViewControllerDidFinish:(SecondViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
}
Et voil ! Compilez et lancez pour observer les diffrentes animations !

23

Navigation entre vues

01_BlocN_iPhone.indd 205

205

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

23

206

Navigation entre vues

NAVIGUER

TOUT EN TANT CONTRL...

Passons maintenant lutilisation dun contrleur de navigation. Voici le schma de navigation que
nous souhaitons atteindre Figure 23.2. Notez que lon pourrait trs facilement mettre en place une
navigation du type de la Figure 23.3.
SecondViewController

ThidViewController

FourthViewController

FifthViewController

Figure 23.2 : La navigation souhaite.

SecondViewController

ThidViewController

FourthViewController

FifthViewController

Figure 23.3 : Une hirarchie possible.

01_BlocN_iPhone.indd 206

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Pour commencer, crez trois nouveaux fichiers de type UIViewController que vous nommerez ThirdViewController, FourthViewController et FifthViewController.
Dans un premier temps, nous allons crer un bouton dans SecondViewController pour afficher le
ThirdViewController. Ajoutez ces lignes dans le viewDidLoad :

// construction du bouton pour aller dans le navigation controller


UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:@selector(goToThirdView:) forControlEvents:UIControlEv
entTouchUpInside];
[button setFrame:CGRectMake(80, 400, 180, 30)];
[button setTitle:@Navigation Controller forState:UIControlStateNormal];
[self.view addSubview:button];
Ensuite, nous allons implmenter goToThirdView: grce presentModalViewController:animated:.
Cependant, nous nallons pas afficher directement la vue de SecondViewController, mais nous allons
crer un contrleur de navigation que nous initialiserons avec une instance de SecondViewController,
puis nous afficherons sa vue. Notez que par dfaut, lanimation est Cover vertical.
Dclarez ceci dans SecondViewController.h :

UINavigationController *navigationController;
ThirdViewController *thirdViewController;
Et noubliez pas dajouter
@class ThirdViewController;
Ensuite, implmentez cette mthode dans le .m :
- (void) goToThirdView:(id)sender {
if (!thirdViewController) 
thirdViewController = [[ThirdViewController alloc]
initWithNibName:@ThirdViewController bundle:nil];
if (!navigationController) 
navigationController = [[UINavigationController alloc]
initWithRootViewController:thirdViewController];
[self presentModalViewController:navigationController animated:YES];
}
Lignes  et , nous testons si les objets ont dj t allous pour viter la fois des fuites, et des
allocations de variables locales inutiles si nous ne les avions pas dclares dans le .h.
Maintenant que notre SecondViewController saffiche (vous pouvez le vrifier en compilant) nous
aimerions mettre un titre notre barre de navigation. Pour cela, rien de plus simple : ajoutez cette
ligne dans le viewDidLoad de ThirdViewController.

self.title = @Third;
Une navigation bar compte trois sections diffrentes comme nous le voyons Figure 23.4.
La section de gauche affiche par dfaut le bouton retour au prcdent contrleur dans la pile de
navigation. Si on choisit dafficher une vue la place, il faut employer leftBarButtonItem.

23

Navigation entre vues

01_BlocN_iPhone.indd 207

207

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

23

208

Navigation entre vues

backBarButtonItem
affich par dfaut avec le titre du prcdent
contrleur, sinon cest leftBarButtonItem

Figure 23.4 : Structure


dune navigation bar.

leftBarButtonItem
avec ici un bouton de
type Done

titleView

rightBarButtonItem
avec ici une vue
personnalise

La section du centre affiche par dfaut le titre du contrleur actuellement affich.


La section de droite naffiche rien par dfaut. Il faut donc se servir de rightBarButtonItem.
Affichons donc un bouton Retour. En effet, ThirdViewController tant le premier view controller de
la pile, il faut pouvoir revenir au SecondViewController qui lui na strictement rien voir avec notre
structure de contrleur de navigation. Pour cela, ajoutez dans le viewDidLoad :

// bouton done
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc]
initWithTitle:@Retour
style:UIBarButtonItemStyleDone
target:self
action:@selector(returnToSecond:)];
self.navigationItem.rightBarButtonItem = doneButton;
[doneButton release];
Puis ajoutez cette mthode pour revenir au SecondViewController :

- (void) returnToSecond:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
Enfin, pour vous rendre dans la vue de FourthViewController, ajoutez un bouton dans le viewDidLoad :

// construction du bouton pour aller dans la quatrime vue


UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:@selector(goToFourthView:)
forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 80, 30)];
[button setCenter:self.view.center];
[button setTitle:@Fourth forState:UIControlStateNormal];
[self.view addSubview:button];
Pour nous rendre dans cette vue, nous passons par une mthode de UINavigationController :
pushViewController:animated:. Ajoutez cette mthode :

01_BlocN_iPhone.indd 208

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

- (void) goToFourthView:(id)sender {
FourthViewController *viewController = [[FourthViewController alloc]
initWithNibName:@FourthViewController bundle:nil];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
}
Noubliez pas limport de FourthViewController dans le .m !
Nous allons construire quasiment lidentique FourthViewController (nous najouterons pas de
bouton Retour mais le laisserons par dfaut, le backBarButtonItem).

- (void)viewDidLoad {
self.title = @Fourth;
// construction du bouton pour aller dans la cinquime vue
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:@selector(goToFifthView:)
forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 80, 30)];
[button setCenter:self.view.center];
[button setTitle:@Fifth forState:UIControlStateNormal];
[self.view addSubview:button];
[super viewDidLoad];
}
- (void) goToFifthView:(id)sender {
FifthViewController *viewController = [[FifthViewController alloc]
initWithNibName:@FifthViewController bundle:nil];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];
}
Noubliez pas limport de FifthViewController dans le .m !
Pour finir, construisons la navigation bar de FifthViewController avec un bouton de droite personnalis avec une barre segments (UISegmentedControl).
Ajoutez ceci dans le viewDidLoad :

- (void)viewDidLoad {
self.title = @Fifth;
// bouton annuler
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]
initWithTitle:@Annuler
style:UIBarButtonItemStyleDone
target:self
action:@selector(returnToFourth:)];

23

Navigation entre vues

01_BlocN_iPhone.indd 209

209

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

23

210

Navigation entre vues

self.navigationItem.leftBarButtonItem = cancelButton;
[cancelButton release];
// Construction dune vue custom pour la droite de la barre
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc]
initWithItems:[NSArray arrayWithObjects:

[UIImage imageNamed:@01-refresh.png],
[UIImage imageNamed:@05-shufe.png],
[UIImage imageNamed:@12-eye.png],
nil]];
[segmentedControl addTarget:self action:@selector(segmentAction:)
forControlEvents:UIControlEventValueChanged];

segmentedControl.frame = CGRectMake(0, 0, 90, 30);


segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
// cration du bouton de droite partir du segmented control
UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc]
initWithCustomView:segmentedControl];
[segmentedControl release];
self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];
[super viewDidLoad];
}
Il ne vous aura pas chapp que nous nemployons pas le bouton de retour par dfaut ! Cest en effet
pour vous montrer la mthode popViewControllerAnimated: pour revenir au prcdent contrleur.
Ajoutez cette mthode :

- (void) returnToFourth:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
Pour grer les segments, ajoutez cette mthode :

- (void) segmentAction:(id)sender {
UISegmentedControl *control = (UISegmentedControl*)sender;
NSLog(@a appuy sur %d, control.selectedSegmentIndex);
// Typiquement, faire un switch case sur le selectedSegmentIndex
}
Enfin, pour voir les mthodes popToViewController:animated: et popToRootViewControllerAnimated:
nous allons ajouter un bouton qui va nous permettre de revenir au ThirdViewController. Dans le
viewDidLoad, ajoutez :

// construction du bouton pour aller dans la troisime vue.


UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];

01_BlocN_iPhone.indd 210

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[button addTarget:self action:@selector(goToThirdView:)


forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(0, 0, 80, 30)];
[button setCenter:self.view.center];
[button setTitle:@Third forState:UIControlStateNormal];
[self.view addSubview:button];
Arrtons-nous un instant sur notre pile de contrleurs : lorsque FifthViewController apparat, voici
ce que lon obtient avec :

NSArray *allControllers = [self.navigationController viewControllers];


Navigation[1566:207] (
<ThirdViewController: 0x5a35740>,
<FourthViewController: 0x5a38640>,
<FifthViewController: 0x5a38e50>
)
Ce qui est somme toute assez logique. Le premier objet de ce tableau est le plus ancien contrleur
de la pile : ThirdViewController. Si nous souhaitons revenir ce premier contrleur directement
depuis FifthViewController, plusieurs solutions :

Solution 1. Utiliser lindex du contrleur afficher (sapplique du moment que lon connat
lindex de lobjet dans la pile que lon souhaite atteindre) :

- (void)goToThirdView:(id)sender {
NSArray *allControllers = [self.navigationController viewControllers];
[self.navigationController popToViewController:[allControllers
objectAtIndex:0] animated:YES];
}

Solution 2. Ici ThirdRootViewController est le RootViewControlleur de notre pile de contrleurs :


- (void)goToThirdView:(id)sender {
[self.navigationController popToRootViewControllerAnimated:YES];
}

Solution 3. La plus gnrale pour accder un contrleur dont on connat le nom de la classe :
- (void)goToThirdView:(id)sender {
NSArray *allControllers = [self.navigationController viewControllers];
UIViewController *theController = nil;
for(UIViewController *vc in allControllers)
if ([vc isKindOfClass:[NSClassFromString(@ThirdViewController) class]]) {
theController = vc;
[self.navigationController
popToViewController:theController animated:YES];
}
}
Finalement, cest assez simple ! Push pour ajouter un contrleur dans la pile, pop pour se rendre un
contrleur dans la pile !

23

Navigation entre vues

01_BlocN_iPhone.indd 211

211

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

24

Utiliser un tab bar

212

24 Utiliser un tab bar


Le tab bar est un lment dinterface incontournable lorsque vous souhaitez prsenter
des donnes diffrentes dans une mme application.Vos vues seront alors accessibles par
une liste donglets !
On trouve le tab bar (UITabBar) dans lapplication iPod, par exemple quand vous choisissez dafficher
votre musique en triant par noms dartiste, morceaux, albums... La Figure 24.1 montre ce que nous
allons raliser.

Figure 24.1 : Exemple dun tab bar.

AFFICHER

UN TAB BAR

Commenons par crer un nouveau projet, de type Window-based Application que vous nommerez
TabBar. Nous allons tout dabord afficher trois contrleurs de vues. Pour cela, ajoutez au projet trois
fichiers de type UIViewController que vous nommerez respectivement FirstViewController, SecondViewController et ThirdViewController (cochez With XIB for user interface).
Pour grer et afficher le tab bar, nous avons recours un tab bar controller de la classe UITabBarController. Pour faire apparatre les diffrents onglets, il faut ajouter des fichiers au tableau viewControllers qui est une proprit dinstance de UITabBarController. Le contrleur lindex 0
correspond celui affich le plus gauche.

01_BlocN_iPhone.indd 212

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ajoutez ces lignes dans lapplication delegate (.h) :

#import <UIKit/UIKit.h>
@interface TabBarAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) UITabBarController *tabBarController;
@end
Ensuite, ajoutez cette ligne dans le .m :

@synthesize tabBarController;
Dans la mthode application:didFinishLaunchingWithOptions;, initialisons notre tab bar controller :

self.tabBarController = [[[UITabBarController alloc] init] autorelease];


Puis, nous allons initialiser trois contrleurs de vue. Vous noterez que pour le deuxime contrleur,
nous lui ajouterons un contrleur de navigation, simplement pour montrer quil est possible de les
combiner.
Commencez par importer les trois contrleurs :

#import FirstViewController.h
#import SecondViewController.h
#import ThirdViewController.h
Ajoutez ces quelques lignes :

FirstViewController *rstViewController = [[FirstViewController alloc]


initWithNibName:@FirstViewController bundle:nil];
rstViewController.title = @Premier;
SecondViewController *secondViewController = [[SecondViewController alloc]
initWithNibName:@SecondViewController bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:secondViewController];
navigationController.title = @Deuxime;
[secondViewController release];
[navigationController setNavigationBarHidden:NO];
ThirdViewController *thirdViewController = [[ThirdViewController alloc]
initWithNibName:@ThirdViewController bundle:nil];
thirdViewController.title = @Troisime;
Puis, il faut ajouter ces contrleurs au tab bar :

tabBarController.viewControllers = [NSArray arrayWithObjects:rstViewController,


navigationController, thirdViewController, nil];

24

Utiliser un tab bar

01_BlocN_iPhone.indd 213

213

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

24

214

Utiliser un tab bar

Lorsque les contrleurs ont t ajouts, il faut librer la mmoire puis ajouter la vue du tab bar la
fentre :

[rstViewController release];
[navigationController release];
[thirdViewController release];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
return YES;

Info
Par dfaut, le titre de chaque onglet sera construit laide de la proprit title de chaque contrleur.
Nous verrons ensuite comment personnaliser cet affichage.
Compilez votre application pour voir apparatre vos trois onglets ! Cest assez facile non ?

SLECTIONNER

UN ONGLET PAR LE CODE

Parfois, il est important de pouvoir slectionner un onglet par le code. En effet, si votre application
possde un onglet ddi lAide, lutilisateur peut y accder seul tout moment, mais vous pouvez
galement souhaiter ly renvoyer si besoin. Pour cela, il faut passer par la proprit selectedViewController ou selectedIndex de votre tab bar contrleur.
Dans le troisime contrleur de vue (ThirdViewController), nous allons ajouter deux boutons :

- (void)viewDidLoad {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:CGRectMake(30, 30, 280, 30)];
[button setTitle:@Premier controleur de la liste forState:UIControlStateNormal];
[button addTarget:self action:@selector(selectFirstViewControllerOfListe:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button2 setFrame:CGRectMake(30, 70, 280, 30)];
[button2 setTitle:@Premier controleur forState:UIControlStateNormal];
[button2 addTarget:self action:@selector(selectFirstViewController:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button2];
[super viewDidLoad];
}
Commenons par slectionner un onglet par son index. En fait, on slectionne longlet selon son
numro daffichage (0 reprsentant celui de gauche).

- (void) selectFirstViewControllerOfListe:(id)sender {
// rfrence vers lapplication delegate pour rcuprer le tab bar controller

01_BlocN_iPhone.indd 214

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

TabBarAppDelegate *appDelegate = (TabBarAppDelegate*)[[UIApplication


sharedApplication] delegate];

UITabBarController *tabController = appDelegate.tabBarController;


// slection de longlet le plus gauche
tabController.selectedIndex = 0;
}
Noubliez pas limport du fichier TabBarAppDelegate.h !
Il faut savoir que vous pouvez changer lordre daffichage de vos onglets, ce que nous verrons par la
suite. Slectionner un onglet par son numro prsente donc des limites, ou vous impose de connatre
son index dans le tab bar. Ainsi, pour choisir un contrleur de vue non plus par lindex de longlet
mais par le nom du contrleur, il faut rcuprer le contrleur partir du nom de sa classe. Aussi,
nous allons dans le deuxime bouton slectionner longlet qui est attach au contrleur de vue
FirstViewController :

- (void) selectFirstViewController:(id)sender {
// rfrence vers lapplication delegate pour rcuprer le tab bar controller
TabBarAppDelegate *appDelegate = (TabBarAppDelegate*)[[UIApplication
sharedApplication] delegate];
UITabBarController *tabController = appDelegate.tabBarController;
// retrouver le contrleur de la classe FirstViewController
// on parcourt le tableau des contrleurs
UIViewController *controllerToSelect = nil;
for (UIViewController *controller in tabController.viewControllers)
if([controller isKindOfClass:NSClassFromString(@FirstViewController)])
{
controllerToSelect = controller; break; // on sort de la boucle
}
tabController.selectedViewController = controllerToSelect;
}
Pour linstant, le comportement est le mme. Nous allons donc ajouter des contrleurs de vues pour
que vous en compreniez lenjeu !

AFFICHER

PLUS DE CINQ CONTRLEURS DE VUES

Un tab bar ne peut pas afficher plus de cinq onglets. Zut, je souhaite en afficher plus... Il faut donc faire
un choix ? Pas du tout ! De manire automatique, lorsque vous ajouterez plus de cinq contrleurs de
vues dans le tableau viewControllers, le cinquime onglet sera remplac par More si votre application est en anglais. En cliquant sur cet onglet, vous aurez donc accs une liste des contrleurs de
vues qui ne peuvent safficher. Crez trois nouveaux contrleurs que vous nommerez FourthViewController, FifthViewController et SixthViewController.

24

Utiliser un tab bar

01_BlocN_iPhone.indd 215

215

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

24

216

Utiliser un tab bar

Ajoutez ces lignes dans lapplication delegate et modifiez les parties en gras :

FourthViewController *fourthViewController = [[FourthViewController alloc]


initWithNibName:@FourthViewController bundle:nil];
fourthViewController.title = @Quatrime;
FifthViewController *fthViewController = [[FifthViewController alloc]
initWithNibName:@FifthViewController bundle:nil];

fthViewController.title = @Cinquime;
SixthViewController *sixthViewController = [[SixthViewController alloc]
initWithNibName:@SixthViewController bundle:nil];

sixthViewController.title = @Sixime;
tabBarController.viewControllers = [NSArray arrayWithObjects:rstViewController,
navigationController, thirdViewController, fourthViewController,
fthViewController, sixthViewController, nil];

[rstViewController release];
[navigationController release];
[thirdViewController release];
[fourthViewController release];
[fthViewController release];
[sixthViewController release];
Noubliez pas les imports de fichiers, mme si vous devez maintenant le faire seul.
Compilez, puis lancez votre application.Vous pourrez cliquer sur More pour accder aux contrleurs
Cinquime et Sixime. Vous trouverez galement un bouton Edit qui modifie lordre des vos onglets.
Modifiez cet ordre et rendez-vous sur longlet nomm Troisime pour bien comprendre la slection
dun onglet que nous avons vue prcdemment.

Info
Par dfaut, tous les onglets sont susceptibles dtre modifis. Vous pouvez choisir ceux modifiables
en les ajoutant dans le tableau customizableViewControllers (proprit dinstance de la classe
UITabBarController) qui par dfaut reoit une copie de la proprit viewControllers. Pour empcher
toute modification, vous pouvez faire pointer ce tableau vers nil.

ET LE DELEGATE ?
Vous pouvez galement implmenter les mthodes de UITabBarDelegate qui vous avertira du dbut de
ldition, de la fin de ldition, de la slection dun contrleur... Vous retrouverez ces mthodes dans la
documentation de UITabBarDelegate.

01_BlocN_iPhone.indd 216

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

COMMENT MODIFIER LORDRE DAFFICHAGE DES ONGLETS LORSQUIL Y EN A CINQ OU MOINS ?


Lorsque vous avez cinq onglets, ou moins, et pas donglet More vous permettant daccder au bouton
ddition, vous pouvez quand mme changer lordre daffichage. Pour cela, vous avez besoin des mthodes
dinstance de UITabBar (vous pouvez accder au tabBar dun contrleur avec tabBarController.tabBar) :
beginCustomizingItems:(NSArray*)items Va afficher une vue modale permettant de rarranger
les onglets spcifis en paramtres. Typiquement, vous appellerez cette mthode en appuyant sur
un bouton diter par exemple.
endCustomizingAnimated:(BOOL)animated Va permettre denlever la vue permettant de modifier
lordre daffichage. Typiquement, vous nemploierez pas cette mthode, car le bouton Done ou
Terminer de la vue modale le fera pour vous automatiquement.
isCustomizing Renvoi un boolen indiquant si lutilisateur est en train dditer ou non le tab bar.

PERSONNALISER

LES ONGLETS

Les onglets sont personnalisables. En effet, par dfaut, le titre de longlet est celui du contrleur quil
reprsente. Mais vous pouvez personnaliser vos onglets en ajoutant une image. Attention cependant,
cette image devra tre en format png, faire 30 30 pixels, et tre transparente. Lorsque longlet est
slectionn, vous remarquerez que limage saffiche en bleu. Ce comportement est gr en interne et
vous ne pouvez le modifier.
Ajoutons au cinquime contrleur de vue un onglet avec un type prdfini. Pour cela, ajoutez les
lignes en gras :

FifthViewController *fthViewController = [[FifthViewController alloc]


initWithNibName:@FifthViewController bundle:nil];
UITabBarItem *tabBarItemFifth = [[UITabBarItem alloc]
initWithTabBarSystemItem:UITabBarSystemItemTopRated tag:5];
fthViewController.tabBarItem = tabBarItemFifth;
[tabBarItemFifth release];
fthViewController.title = @Cinquime; // supprimer
Vous avez le choix entre plusieurs types : UITabBarSystemItemTopRated affiche une toile, UITabBarSystemItemRecents affiche une horloge... Les titres sont galement dfinis par dfaut, et vous trouverez les autres types dans la documentation de la classe UITabBarItem.
Il est galement possible de faire apparatre votre propre image ! Pour cela, allons sur le site glyphish.
com, qui propose un large choix dicnes gratuites placer dans vos barres de navigation.

SixthViewController *sixthViewController = [[SixthViewController alloc] initWithNib


Name:@SixthViewController bundle:nil];
UITabBarItem *tabBarItemSixth = [[UITabBarItem alloc] initWithTitle:@Sixime
image:[UIImage imageNamed:@17-bar-chart.png] tag:6];
sixthViewController.tabBarItem = tabBarItemSixth;
[tabBarItemSixth release];
sixthViewController.title = @Sixime;

24

Utiliser un tab bar

01_BlocN_iPhone.indd 217

217

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

24

218

Utiliser un tab bar

Pour finir, nous allons voir comment ajouter un badge sur un onglet. Ce badge permet, par exemple,
dafficher le nombre darticles non lus dans le cadre dun lecteur de flux RSS. Pour cela, nous allons
ajouter un bouton qui incrmentera la valeur du badge dans le contrleur de vues FourthViewController :

- (void)viewDidLoad {
// on met le badge la valeur 0
self.tabBarItem.badgeValue = @0;
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setFrame:CGRectMake(30, 30, 100, 30)];
[button setTitle:@++ forState:UIControlStateNormal];
[button addTarget:self action:@selector(addBadgeValue:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
[super viewDidLoad];
}
- (void) addBadgeValue:(id)sender {
// on rcupre la valeur du badge
NSInteger theInteger = [self.tabBarItem.badgeValue integerValue];
// on incrmente la valeur
theInteger++;
// on afche cette nouvelle valeur
self.tabBarItem.badgeValue = [NSString stringWithFormat:@%d, theInteger];
}

01_BlocN_iPhone.indd 218

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

25

Utilisation dun timer pour dplacer une vue


25

Utilisation dun timer pour dplacer une vue

Dans cette fiche, nous allons voir comment utiliser les timers (NSTimer) pour dplacer une
vue reprsentant une balle sur lcran. Nous grerons galement les collisions de la balle
sur un objet.

Info
Cette fiche est un prambule pour raliser ce que lon appelle des game loop. Cest en fait une mthode
appele de manire rgulire par un timer et qui met jour des donnes. Typiquement, dans un jeu
vido, cette mthode mettra jour le dessin des personnages, des lments de dcor...
Core Animation est ncessaire dans le cas o vous souhaiteriez simplement dplacer une vue pour
ajouter une animation ponctuelle. Cest une question de performance (voir Fiche 26).
Comme notre habitude, crez un projet de type Windowbased Application que vous nommerez TimerBall, et ajoutez-y
un RootViewController.
Nous allons construire une interface comme la Figure 25.1.
Lancez-vous, et au moindre doute rfrez-vous ce code :

- (void)viewDidLoad {
ballImageView = [[UIImageView alloc]
initWithFrame:CGRectMake(0, 0, LARGEUR_
HAUTEUR_BALLE, LARGEUR_HAUTEUR_BALLE)];
ballImageView.image = [UIImage
imageNamed:@balle.png];
[self.view addSubview:ballImageView];
wallImageView = [[UIImageView alloc]
initWithFrame:CGRectMake(130, 110, 70, 110)];

wallImageView.image = [UIImage
imageNamed:@mur_de_briques.jpg];

[self.view addSubview:wallImageView];

Figure 25.1 : La vue obtenir.

UIButton *startStopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];


[startStopButton setTitle:@Start forState:UIControlStateNormal];
[startStopButton setTitle:@Stop forState:UIControlStateSelected];
[startStopButton addTarget:self action:@selector(startStop:)
forControlEvents:UIControlEventTouchUpInside];
[startStopButton setFrame:CGRectMake(20, 410, 60, 30)];
[self.view addSubview:startStopButton];
ballVelocity = CGPointMake(7, 3); 
topSide = wallImageView.frame.origin.y; 

25

Utilisation dun timer pour dplacer une vue

01_BlocN_iPhone.indd 219

219

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

25

220

Utilisation dun timer pour dplacer une vue

bottomSide = wallImageView.frame.origin.y + wallImageView.frame.size.height;


leftSide = wallImageView.frame.origin.x;
rightSide = wallImageView.frame.origin.x + wallImageView.frame.size.width;
[super viewDidLoad];
}
Tout ceci a dj t vu dans les anciennes fiches. Prcisons cependant quil faut ajouter ces lignes pour
dfinir la taille de la balle ainsi que la frquence de rafrachissement :

#dene LARGEUR_HAUTEUR_BALLE 30.0


#dene FREQUENCE_RAFRAICHISSEMENT 30.0 // Hz
De plus, on dfinit ligne  ce que lon appelle ballVelocity, qui correspond au dplacement de la
balle chaque appel de la mthode de rafrachissement par le timer. Ici, la balle se dplacera donc de
7 pixels en x et 3 en y.
Ensuite, pour grer la collision sur le mur plac sur lcran, on calcule les points correspondants
chaque coin du mur partir de la ligne .
Le bouton Start/Stop va nous permettre de lancer ou mettre en pause le dplacement de la balle.
Pour cela, nous allons le positionner dans ltat slectionn pour animer la balle, puis dselectionn pour
arrter. Voici quoi la mthode doit ressembler :

- (void) startStop:(id)sender {
UIButton *but = (UIButton*)sender;
if (but.selected) { 
[refreshTimer invalidate];
refreshTimer = nil; 
}
else { 
refreshTimer = [NSTimer scheduledTimerWithTimeInterval:1/FREQUENCE
_RAFRAICHISSEMENT target:self selector:@selector(refresh:)
userInfo:nil repeats:YES];
}
[but setSelected:![but isSelected]];
}
Pour rappel, voici le .h :

#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
UIImageView *ballImageView, *wallImageView;
CGPoint ballVelocity;
NSTimer *refreshTimer;
int topSide, bottomSide, leftSide, rightSide;
}
@end

01_BlocN_iPhone.indd 220

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ligne , si le bouton est slectionn, alors le texte affich est Stop. Cela signifie donc quil faut mettre
en pause le dplacement, et donc arrter le timer avec invalidate.

Info
Faire pointer vers nil le timer ligne  est un automatisme prendre lorsque vous librez des objets.
En effet, imaginez un objet unObjet dclar en variable globale et utilis plusieurs endroits dans le
code.
Lorsque vous faites un release sur unObjet, unObjet ne pointera pas vers nil (qui est un pointeur nul)
mais sur une adresse dun objet nexistant plus en mmoire. Donc, si vous faites la chose suivante :

[unObjet release];
...
[unObjet uneMethode];
Vous aurez une erreur et votre application plantera. Par contre, tant donn que les messages vers
nil sont ignors, vous naurez aucun bogue en crivant :

[unObjet release];
unObjet = nil;
...
[unObjet uneMethode]; // ne plantera pas
Ligne , lorsque le bouton affiche Start, on lance le timer qui appellera la mthode de RootViewController
spcifi par target:self : - (void)refresh:(NSTimer*)theTimer de manire rpte avec repeats:YES.
Cette mthode sera appele toutes les 1/30 secondes, soit toutes les 0,033 secondes environ.

Info
Vous souhaitez passer un paramtre la mthode appele par le timer ? Par exemple, imaginons une
mthode de rafrachissement des graphismes, appele par un timer et commune plusieurs vues,
trois boutons. Comment spcifier quelle est la vue concerne par le timer ? Il faut utiliser userInfo :

refreshTimer = [NSTimer scheduledTimerWithTimeInterval:1/FREQUENCE_RAFRAICHISSEMENT


target:self selector:@selector(refresh:) userInfo:button1 repeats:YES];
avec

- (void)refresh:(NSTimer*)theTimer {
UIButton *boutonAAnimer = (UIButton*)[theTimer userInfo];
// faire quelque chose avec le bouton
}

Astuce
Et pour passer plusieurs paramtres ? Rien de plus simple, utilisez un tableau (NSArray) ou un
dictionnaire (NSDictionary) !

25

Utilisation dun timer pour dplacer une vue

01_BlocN_iPhone.indd 221

221

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

25

222

Utilisation dun timer pour dplacer une vue

Enfin, implmentons la mthode appele intervalles rguliers. Nous allons faire deux tests principaux : tester si la balle touche les bords de lcran puis tester si elle touche le mur. Le but de ces tests
est de changer le signe du dplacement en x ou en y. En fait, lorsque la balle touche le bord de lcran
droit ou gauche, il faut la faire rebondir dans son dplacement en x (horizontal). On change donc le
signe de ballVelocity.x. De mme pour les bords hauts et bas avec ballVelocity.y.

- (void) refresh : (NSTimer*)timer {


if(ballImageView.center.x - LARGEUR_HAUTEUR_BALLE/2.0 < 0.0 
||

ballImageView.center.x + LARGEUR_HAUTEUR_BALLE/2.0 > 320.0)


ballVelocity.x = -ballVelocity.x;
if(ballImageView.center.y - LARGEUR_HAUTEUR_BALLE/2.0 < 0.0
||

ballImageView.center.y + LARGEUR_HAUTEUR_BALLE/2.0 > 480.0)


ballVelocity.y = -ballVelocity.y;
//On a besoin de savoir si la nouvelle position touche le mur, mais il faut
garder lancienne position pour faire des calculs

CGRect newFrame = ballImageView.frame;


// on calcule la nouvelle position
newFrame.origin.x += ballVelocity.x;
newFrame.origin.y += ballVelocity.y;
// Attention, la balle na pas encore bouge. On regarde juste si elle
toucherait le mur

if (CGRectIntersectsRect(newFrame, wallImageView.frame)) { 
// haut ou bas du mur
if (ballImageView.center.y < topSide || ballImageView.center.y > bottomSide)
ballVelocity.y = -ballVelocity.y;
// gauche ou droit du mur
if (ballImageView.center.x < leftSide || ballImageView.center.x > rightSide)
ballVelocity.x = -ballVelocity.x;
} else {
// On ne bouge la balle que sil ny a pas de collision
ballImageView.frame = newFrame;
}
}
Vous distinguez deux mthodes :
La premire, plus nave partir de la ligne  qui dtecte si la balle a touch lcran de liPhone.
La seconde, ligne , quant elle utilise une mthode de CoreGraphics : CRRectIntersectsRect(CGRect
rstRect, CGRect secondRect) qui retourne true si les deux rectangles passs en paramtre se
touchent.

01_BlocN_iPhone.indd 222

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Finissons par un peu de thorie sur les timers et leur place dans le systme. Les timers fonctionnent
avec les objets de la classe NSRunLoop. Ces objets contrlent les boucles qui attendent une entre
comme un vnement sur la souris ou le clavier sur un ordinateur. Les objets de NSRunLoop se servent
donc des timers pour dterminer le temps pendant lequel ils doivent attendre. Lorsque ce temps est
coul, la run loop lance le timer et regarde les nouvelles entres.
Pour rsumer, une run loop est lance pour planifier des tches et coordonner la rception dvnements entrants. En fait, cela permet de garder le thread occup quand il y a des tches raliser, et de
le mettre en veille lorsquil ny a rien faire.

Info
Il existe une alternative aux timers, par exemple :

[self performSelector:@selector(aMethod:) withObject:anObjet afterDelay:delay];


Cela permet dappeler la mthode aMethod: aprs un temps donn par delay.
Vous laurez compris, il ne faut plus utiliser sleep(unTemps) !
Et ne ngligez jamais limplmentation de dealloc :

- (void)dealloc {
[refreshTimer invalidate];
refreshTimer = nil;
[wallImageView release];
[ballImageView release];
[super dealloc];
}

Astuce
Ici, le mur est statique. Si vous souhaitez dtecter la collision avec un objet en mouvement, il faut
inclure dans la mthode du timer le calcul des quatre variables topSide, bottomSide, leftSide et
rightSide.

25

Utilisation dun timer pour dplacer une vue

01_BlocN_iPhone.indd 223

223

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

Core Animation

224

26 Core Animation
Vous pouvez trs facilement mettre en place des animations, plus ou moins compliques,
pour faire vivre votre application. En effet, faire arriver ses boutons en glissant, raliser
des fondus, des dplacements de vues peut rendre votre application plus chaleureuse
avec seulement quelques lignes de code !
Dans un premier temps, nous allons nous intresser aux mthodes de haut niveau de Core Animation
(ces mthodes sont incluses dans la classe UIView). Ensuite, nous raliserons une sorte dintroduction
au bas niveau de ce framework.
Tout dabord, soyez attentifs au fait que Core Animation nest pas de lOpenGL. Mme si vous trouvez
des animations de dplacement en 3D, ce nest pas un moteur de rendu 3D. Le framework de cette
fiche vous sera trs utile pour crer des animations simples ou non. Par contre, recourir cette
technologie pour raliser un jeu complet pourrait se rvler tre un challenge !
Core Animation est un sujet trs vaste, que nous ne pourrons pas dvelopper entirement dans cet
ouvrage. Considrez donc cette fiche comme une initiation et reportez-vous la documentation pour
approfondir.

UTILISER
CRATION

DES ANIMATIONS DE HAUT NIVEAU

DU PROJET

Crez un nouveau projet de type Window-based Application que vous nommerez Animations.Ajoutez
ensuite deux fichiers de type UIViewController que vous nommerez RootViewController et
UIViewAnimations. Comme notre habitude, affichez la vue du RootViewController par-dessus la
window dans notre application delegate. Ensuite, dans RootViewController, nous allons ajouter un
bouton pour afficher notre vue de UIViewAnimations. Perdu ?

RootViewController.h
#import <UIKit/UIKit.h>
@class UIViewAnimations;
@interface RootViewController : UIViewController {
UIViewAnimations *viewAnimations;
}
@end
RootViewController.m
#import RootViewController.h
#import UIViewAnimations.h
@implementation RootViewController
- (void)viewDidLoad {
UIButton *buttonForViewAnimations =
[UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonForViewAnimations setFrame:CGRectMake(30, 30, 130, 30)];
[buttonForViewAnimations setTitle:@View animations

01_BlocN_iPhone.indd 224

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

forState:UIControlStateNormal];
[buttonForViewAnimations addTarget:self action:@selector(displayViewAnimations:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonForViewAnimations];

[super viewDidLoad];
}
- (void)displayViewAnimations:(id)sender {
if(!viewAnimations)
viewAnimations = [[UIViewAnimations alloc]
initWithNibName:@UIViewAnimations bundle:nil];
[self.view addSubview:viewAnimations.view];
}
- (void)dealloc {
[viewAnimations release];
[super dealloc];
}

Info
Pas de panique, on ne gre pas ici le retour de la vue viewAnimations, ce nest pas le propos !
Dans UIViewAnimations, nous allons grer plusieurs animations :

une animation dune vue qui va changer de couleur de manire alatoire et en douceur ;
une animation dune vue pour changer sa transparence ;
une animation dun bouton qui tremble ;
une animation de zoom dune image ;
une animation de dplacement dune vue.

Commencez par dclarer les variables ncessaires dans UIViewAnimations.h :

#import <UIKit/UIKit.h>
@interface UIViewAnimations : UIViewController {
UIView *viewForColorAnimation, *viewForAlphaAnimation;
UIButton *buttonForWiggleAnimation;
UIImageView *imageViewForZoomAnimation;
UIView *viewForMultiplesAnimations;
NSTimer *timerForColorAnimation;
BOOL canAnimate;
}
@end

26

Core Animation

01_BlocN_iPhone.indd 225

225

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

226

Core Animation

CHANGER

LES COULEURS

Pour cette animation, nous allons utiliser un timer qui appellera toutes les 4 secondes une mthode
qui animera les transitions de couleurs. Commenons par dclarer un bouton de Start/Stop pour
commencer/arrter les animations et la vue qui changera de couleur. Dans le viewDidLoad, ajoutez :

// vue pour lanimation de la couleur


viewForColorAnimation = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 60, 60)];
[self.view addSubview:viewForColorAnimation];
UIButton *buttonToLaunchStopAnimations =
[UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonToLaunchStopAnimations setFrame:CGRectMake(100, 440, 70, 30)];
[buttonToLaunchStopAnimations addTarget:self action:@selector(launchStopAnim:)
forControlEvents:UIControlEventTouchUpInside];
[buttonToLaunchStopAnimations setTitle:@Start forState:UIControlStateNormal];
[buttonToLaunchStopAnimations setTitle:@Stop forState:UIControlStateSelected];
[self.view addSubview:buttonToLaunchStopAnimations];
Ensuite, dfinissons la mthode start/stop avec lappel au timer :

- (void) launchStopAnim:(id)sender {
UIButton *button = (UIButton*)sender;
if (![button isSelected]) {
canAnimate = YES;
// on lance les anims
// Couleur
// On lance tout de suite une animation
[self performSelector:@selector(changeColor:) withObject:nil];
// on lance le timer
timerForColorAnimation = [NSTimer scheduledTimerWithTimeInterval:4.0
target:self selector:@selector(changeColor:) userInfo:nil repeats:YES];
}
else {
canAnimate = NO;
// on stoppe lanim de la couleur
[timerForColorAnimation invalidate];
timerForColorAnimation = nil;
}
// on change ltat du bouton
[button setSelected:![button isSelected]];
}

01_BlocN_iPhone.indd 226

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Toutes les 4 secondes, nous appellerons donc la mthode changeColor:. Explicitons-la :

- (void)changeColor:(NSTimer*)theTimer {
[UIView beginAnimations: @Color animation context: NULL]; 
[UIView setAnimationDuration: 3.5]; 
CGFloat redLevel
= rand() / (oat) RAND_MAX; 
CGFloat greenLevel
= rand() / (oat) RAND_MAX;
CGFloat blueLevel
= rand() / (oat) RAND_MAX;
viewForColorAnimation.backgroundColor = [UIColor colorWithRed: redLevel
green: greenLevel blue: blueLevel alpha: 1.0]; 

[UIView commitAnimations]; 

}
Ligne , on commence les animations, avec comme nom danimation Color animation. Ce nom na
pas dimportance, il permet de sy retrouver dans votre code et/ou de grer plusieurs animations en
mme temps (rfrez-vous lanimation multiple plus loin).
Ligne , on choisit simplement la dure de lanimation en seconde.
Ligne  et suivantes, on dfinit une nouvelle couleur au hasard. Ligne , on change la couleur de
notre vue avec la nouvelle. Enfin, ligne , on lance lanimation !

Info
Ce quil faut comprendre : avant dentrer dans le bloc danimation, la vue viewForColorAnimation a
une certaine couleur. Ensuite, ligne , on dfinit une nouvelle couleur alatoirement. Cette nouvelle
couleur ne sera pas applique instantanment mais avec animation ! La transition se fera donc en
douceur en 3,5 secondes.
Compilez et lancez pour observer le comportement de votre vue !

CHANGER

LA TRANSPARENCE

Mme combat pour cette animation. Une grosse diffrence cependant : nous nutiliserons pas de timer.
Oui, mais alors comment faire rpter cette animation ? Avec la proprit animationRepeatCount !
Ajoutez dans le .h :

(void)
(void)
(void)
(void)

startAlphaAnimation;
startWiggleAnimation;
startZoomAnimation;
startMultipleAnimations;

Puis dans le .m, dfinissez :

- (void) startAlphaAnimation {
#dene alphaStart 0.9
#dene alphaStop 0.1
// point de dpart
viewForAlphaAnimation.alpha = alphaStart;

26

Core Animation

01_BlocN_iPhone.indd 227

227

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

228

Core Animation

[UIView beginAnimations: @Alpha animation context: NULL];


[UIView setAnimationDuration: 2.0];
[UIView setAnimationRepeatAutoreverses:YES]; 
[UIView setAnimationRepeatCount:1E100]; 
viewForAlphaAnimation.alpha = alphaStop; // sarrte ici et recommence
[UIView commitAnimations];
}
Ligne , on dfinit le comportement de notre animation : elle va se rpter quasi indfiniment (1E100
= 1*10100).
Ligne , on spcifie que notre animation va aller et venir : lalpha de dpart tant de 0.9, lanimation
va lamener 0.1. Puis, lanimation va remettre la valeur de transparence dorigine de la vue en lanimant puis va recommencer son cycle. Si on enlve la ligne , lanimation de notre vue va se
comporter comme suit : lalpha va passer de 0.9 0.1 puis brusquement de 0.1 0.9, sans animation.
Essayez pour voir (impratif) !
Lanimation ne se lance pas ? videmment, il faut rajouter la ligne en gras dans launchStopAnim: :

// on lance le timer
timerForColorAnimation = [NSTimer scheduledTimerWithTimeInterval:4.0 target:self
selector:@selector(changeColor:) userInfo:nil repeats:YES];
[self startAlphaAnimation];
Et initialiser la vue dans le viewDidLoad (initialisons-les toutes pour que ce soit fait !) :

// vue pour lanimation de la transparence


viewForAlphaAnimation = [[UIView alloc] initWithFrame:CGRectMake(100, 30, 60, 60)];
[viewForAlphaAnimation setBackgroundColor:[UIColor redColor]];
[self.view addSubview:viewForAlphaAnimation];
// bouton qui va trembler
buttonForWiggleAnimation = [[UIButton buttonWithType:UIButtonTypeRoundedRect]
retain];
[buttonForWiggleAnimation setFrame:CGRectMake(30, 100, 70, 30)];
[buttonForWiggleAnimation setTitle:@;- forState:UIControlStateNormal];
[self.view addSubview:buttonForWiggleAnimation];
// vue pour le zoom
imageViewForZoomAnimation = [[UIImageView alloc] initWithFrame:CGRectMake(120,
100, 100, 100)];
imageViewForZoomAnimation.image = [UIImage imageNamed:@logo_ipup_losange.png];
[self.view addSubview:imageViewForZoomAnimation];
//vue pour lanimation multiple
viewForMultiplesAnimations = [[UIView alloc] initWithFrame:CGRectMake(0, 160, 40, 40)];
viewForMultiplesAnimations.backgroundColor = [UIColor blueColor];
[self.view addSubview:viewForMultiplesAnimations];

01_BlocN_iPhone.indd 228

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Enfin, comment arrter les animations ? En jouant sur le CALayer de chaque vue. Cette entit vous
permet de raliser les animations que ce soit en haut niveau, ou en plus bas niveau.
Importez le framework Quartz Core et ajoutez cette ligne dans UIViewAnimations.h :

#import <QuartzCore/QuartzCore.h>
Puis ajoutez les lignes en gras :

else {
canAnimate = NO;
// on stoppe lanim de la couleur
[timerForColorAnimation invalidate];
timerForColorAnimation = nil;
// on arrte toutes les animations
for (UIView *view in [self.view subviews]) {
[view.layer removeAllAnimations];
}
}

Info
Il est primordial, indispensable, darrter vos animations ds lors que vous ne vous en servez plus,
notamment lorsque vous quittez la vue !

FAIRE

TREMBLER UN BOUTON

Ajoutez cette mthode pour faire trembler le bouton comme sur lcran daccueil de votre iPhone :

- (void) startWiggleAnimation {
#dene RADIANS(degrees) ((degrees * M_PI) / 180.0)
CGAfneTransform initialPosition =
CGAfneTransformRotate(CGAfneTransformIdentity, RADIANS(-7.0)); 

CGAfneTransform nalPosition =
CGAfneTransformRotate(CGAfneTransformIdentity, RADIANS(7.0));

// point de dpart :
buttonForWiggleAnimation.transform = initialPosition; 
[UIView
[UIView
[UIView
[UIView

beginAnimations:@Wiggle animation context:NULL];


setAnimationRepeatAutoreverses:YES];
setAnimationRepeatCount:1E100];
setAnimationDuration:0.1];

buttonForWiggleAnimation.transform = nalPosition; // nit ici et recommence


[UIView commitAnimations];
}

26

Core Animation

01_BlocN_iPhone.indd 229

229

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

230

Core Animation

Cette fois-ci, nous agissons sur la proprit transform de notre vue (ligne ). Cette proprit se
rvle dailleurs trs pratique pour transformer une vue (la dplacer, appliquer un zoom, la faire
tourner...). Cette transformation se dfinit ligne  et suivante, et attend en paramtre un angle en
radian. Pour rappel, 180 = radian.
Noubliez pas lajout de la ligne en gras pour lancer lanimation :

[self startAlphaAnimation];
[self startWiggleAnimation];

RALISER

UN ZOOM

Cette fois-ci, plus rien ne devrait vous surprendre :

- (void) startZoomAnimation {
CGAfneTransform initialZoom = CGAfneTransformMakeScale(1.0, 1.0);
CGAfneTransform nalZoom = CGAfneTransformMakeScale(0.6, 0.6);
// point de dpart :
imageViewForZoomAnimation.transform = initialZoom;
[UIView
[UIView
[UIView
[UIView
[UIView

beginAnimations:@Zoom animation context:NULL];


setAnimationRepeatAutoreverses:YES];
setAnimationCurve:UIViewAnimationCurveEaseIn]; 
setAnimationRepeatCount:1E100];
setAnimationDuration:0.6];

imageViewForZoomAnimation.transform = nalZoom; // nit ici et recommence


[UIView commitAnimations];
}
La seule diffrence se situe ligne . On y dfinit la courbe de vitesse de lanimation. Voici les quatre
types possibles :

UIViewAnimationCurveEaseInOut Lanimation commence doucement, acclre jusque moiti, puis


dclre jusque sa fin.

UIViewAnimationCurveEaseIn Lanimation commence doucement, puis acclre tout au long de


la progression.

UIViewAnimationCurveEaseOut Lanimation commence rapidement puis ralentit vers la fin.


UIViewAnimationCurveLinear Lanimation est linaire.
Vous navez pas oubli lajout de la ligne en gras ?

[self startWiggleAnimation];
[self startZoomAnimation];

RALISER

PLUSIEURS ANIMATIONS ENCHANES

Pour finir avec ces mthodes, nous allons raliser un chemin danimation : nous commencerons par
dplacer une vue, la faire tourner de 180 degrs, la faire se redplacer en changeant sa transparence
puis la ramener son point de dpart.

01_BlocN_iPhone.indd 230

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Pour cela, nous allons utiliser le paramtre context de lanimation pour passer le numro de lanimation.

Info
Nous pourrions tout fait nous servir ici des noms des animations pour raliser ce chemin.
Dfinissez cette mthode :

#pragma mark #pragma mark multiple animations


- (void) startMultipleAnimations {
CGPoint nalPosition = CGPointMake(300, 200);
CGPoint initialPosition = CGPointMake(0, 160);
viewForMultiplesAnimations.center = initialPosition;
[UIView beginAnimations:@First context:[NSNumber numberWithInt:1]];
[UIView setAnimationDuration:3.0];
[UIView setAnimationDelegate:self]; 
[UIView setAnimationDidStopSelector:@selector
(multipleAnimationEnded:nished:context:)]; 
viewForMultiplesAnimations.center = nalPosition;
[UIView commitAnimations];
}
Et noubliez pas son appel :

[self startZoomAnimation];
[self startMultipleAnimations];
Comme vous le voyez ligne , lanimation a un dlgu. En fait, cette fois-ci, nous allons recourir
une mthode dlgue appele lorsque lanimation sera termine ligne . Dans cette mthode, nous
ferons un switch case sur le numro de lanimation et aiguillerons la prochaine animation. Pour les
connaisseurs, cest une machine tat !

// deuxime animation : retournement 180 degrs


- (void) startSecondAnimation {
CGAfneTransform initialPosition = CGAfneTransformMakeRotation(RADIANS(0));
CGAfneTransform nalPosition = CGAfneTransformMakeRotation(RADIANS(180));
viewForMultiplesAnimations.transform = initialPosition;
[UIView beginAnimations:@Second context:[NSNumber numberWithInt:2]];
[UIView setAnimationDuration:1.5];
[UIView setAnimationDelegate:self];

26

Core Animation

01_BlocN_iPhone.indd 231

231

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

Core Animation

232

[UIView setAnimationDidStopSelector:@selector
(multipleAnimationEnded:nished:context:)];

viewForMultiplesAnimations.transform = nalPosition;
[UIView commitAnimations];
}
// troisime animation : dplacement + changement de transparence
- (void) startThirdAnimation {
CGPoint nalPosition = CGPointMake(30, 400);
[UIView beginAnimations:@Third context:[NSNumber numberWithInt:3]];
[UIView setAnimationDuration:3.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector
(multipleAnimationEnded:nished:context:)];
viewForMultiplesAnimations.center = nalPosition;
[UIView commitAnimations];
[UIView beginAnimations:@ThirdBis context:NULL];
[UIView setAnimationDuration:3.0];
viewForMultiplesAnimations.alpha = 0.2;
[UIView commitAnimations];
}
// dernire animation : retour au dbut
- (void) returnToStartAnimation {
CGPoint nalPosition = CGPointMake(0, 160);
[UIView beginAnimations:@Fourth context:[NSNumber numberWithInt:4]];
[UIView setAnimationDuration:3.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(
multipleAnimationEnded:nished:context:)];
viewForMultiplesAnimations.center = nalPosition;
[UIView commitAnimations];
[UIView beginAnimations:@FourthBis context:NULL];
[UIView setAnimationDuration:3.0];
viewForMultiplesAnimations.alpha = 1.0;
[UIView commitAnimations];
}

01_BlocN_iPhone.indd 232

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// mthode appele la n de chaque animation


- (void) multipleAnimationEnded:(NSString *)animationID
nished:(NSNumber *)nished context:(void *)context
{
NSNumber *numberId = (NSNumber*)context;
// rcupration du numro de lanimation qui a appel cette mthode
int idOfAnimation = [numberId intValue];
if(canAnimate)
{
switch (idOfAnimation) {
case 1:
[self startSecondAnimation];
break;
case 2:
[self startThirdAnimation];
break;
case 3:
[self returnToStartAnimation];
break;
case 4:
{
[viewForMultiplesAnimations setTransform:CGAfneTransformIdentity];
// on recommence
[self startMultipleAnimations];
}
break;
default:
break;
}
}
}
On remarque que lon peut lancer plusieurs animations en mme temps (dplacement et changement
de transparence) en dclarant simplement autant de blocs danimations que danimations dsires !

RALISER

DES ANIMATIONS EN CREUSANT

Pour cette partie, nous allons solliciter Core Animation dans une forme moins tape lil. En effet,
vous savez que plus on descend en terme de niveau de programmation, plus le contrle sur ce que
lon fait est prcis. Ainsi, nous allons raliser ici une animation qui va dcouper une image en plusieurs
petits carrs et les faire se dplacer en changeant leur transparence... Ce code sinspire du travail de
Bill Dudney (http://bill.dudney.net/roller/objc/).
Notre objectif est de raliser quelque chose de comparable la Figure 26.1.

26

Core Animation

01_BlocN_iPhone.indd 233

233

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

234

Core Animation

Temps

Figure 26.1 : Trame de lanimation de dcoupage.


Pour ce faire :
1. Crez un nouveau fichier de type UIViewController et nommez-le Layers.
2. Dans le RootViewController, crez un nouveau bouton, comme pour afficher UIViewAnimations et ajoutez une action pour lafficher.
3. Vrifiez en compilant quune vue vierge saffiche bien. Si cest le cas, passez la suite, sinon
revenez la section Cration du projet en ladaptant.
Pour faire ces animations, nous allons nous appuyer sur les CALayer. Un CALayer, cest un peu lUIView
de Core Animation. Cest le point de dpart pour toute animation.
Dans Layers.h, ajoutez ces lignes :

#import <UIKit/UIKit.h>
@interface Layers : UIViewController {
// layer pour lanimation
CALayer *imageLayer;
CGImageRef imageRef;
}
@property (nonatomic, retain) CALayer *imageLayer;
- (CGImageRef)scaleAndCropImage:(UIImage *)fullImage;
@end
Noubliez pas dans le .m :

@synthesize imageLayer;
Ensuite, nous allons charger notre vue dans la mthode loadView pour changer (elle est appele avant
le viewDidLoad) :

- (void)loadView {
[super loadView];
// Construction du layer qui va recevoir lanimation

01_BlocN_iPhone.indd 234

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

self.imageLayer = [CALayer layer];


self.imageLayer.frame = CGRectMake(XPosition, YPosition, LargeurMax, HauteurMax);
self.imageLayer.contentsGravity = kCAGravityResizeAspectFill;
self.imageLayer.masksToBounds = YES;
[self.view.layer addSublayer:self.imageLayer];
}
Puis, nous allons charger limage et lafficher dans notre CALayer. Toujours dans le loadView, ajoutez :

// image que lon va transformer


UIImage *image = [UIImage imageNamed:@logo_ipup_losange.png];
// on redimensionne limage et renvoie un CGImageRef
imageRef = [self scaleAndCropImage:image];
// on place limage dans le layer
imageLayer.contents = (id)imageRef;
Ensuite, crons un bouton qui nous permettra de lancer lanimation. Ajoutez toujours au mme
endroit :

// bouton pour lancer lanimation


UIButton *buttonToPop = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonToPop setFrame:CGRectMake(130, 400, 70, 30)];
[buttonToPop setTitle:@Pop forState:UIControlStateNormal];
[buttonToPop addTarget:self action:@selector(pop:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonToPop];
Ah tiens, il y a des valeurs quil ne connat pas ! Qu cela ne tienne... Ajoutez ces lignes en dessous
de limport :

#dene
#dene
#dene
#dene
#dene
#dene

LargeurMax 300.0f
HauteurMax 300.0f
XPosition 10.0f
YPosition 20.0f
nombreDeCarreX 10.0f
nombreDeCarreY 10.0f

Vous lavez remarqu galement, il faut dfinir la mthode dont la signature est scaleAndCropImage:.
Cest un peu compliqu car elle fait appel Core Graphics dont le langage est un peu particulier (se
rapprochant de Carbon).

// mthode pour redimensionner limage et la convertir en CGImageRef


- (CGImageRef)scaleAndCropImage:(UIImage *)fullImage {
CGSize imageSize = fullImage.size;
CGFloat scale = 1.0f;
CGImageRef subimage = NULL;
if(imageSize.width > imageSize.height) {
// La hauteur est plus petite que la largeur

26

Core Animation

01_BlocN_iPhone.indd 235

235

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

236

Core Animation

scale = HauteurMax / imageSize.height;


CGFloat offsetX = ((scale * imageSize.width - LargeurMax) / 2.0f) / scale;
CGRect subRect = CGRectMake(offsetX, 0.0f, imageSize.width - (2.0f *
offsetX), imageSize.height);
subimage = CGImageCreateWithImageInRect([fullImage CGImage], subRect);
} else {
// La largeur est plus petite que la hauteur
scale = LargeurMax / imageSize.width;
CGFloat offsetY = ((scale * imageSize.height - HauteurMax) / 2.0f) / scale;
CGRect subRect = CGRectMake(0.0f, offsetY, imageSize.width, imageSize.height
- (2.0f * offsetY));
subimage = CGImageCreateWithImageInRect([fullImage CGImage], subRect);
}
// on zoom limage selon LargeurMax et HauteurMax dnis au dessus
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, LargeurMax, HauteurMax,
8, 0, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGRect rect = CGRectMake(0.0f, 0.0f, LargeurMax, HauteurMax);
CGContextDrawImage(context, rect, subimage);
CGContextFlush(context);
// on obtient la nouvelle image
CGImageRef scaledImage = CGBitmapContextCreateImage(context);
CGContextRelease (context);
CGImageRelease(subimage);
subimage = NULL;
subimage = scaledImage;
return subimage;
}
Maintenant, dfinissons laction raliser lorsque nous appuyons sur le bouton. Premirement, nous
allons crer un tableau qui contiendra tous les petits carrs de notre image que nous allons dcouper :
(jusqu ce que je vous dise le contraire, tous les morceaux de code iront dans cette mthode, dans
le if) :

// L o tout commence...
- (void)pop:(id)sender {
if(nil != imageLayer.contents) {
//on rcupre la taille de limage dcouper
CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef),
CGImageGetHeight(imageRef));
NSMutableArray *layers = [NSMutableArray array];
}
}

01_BlocN_iPhone.indd 236

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ensuite, nous allons dcouper notre image en carrs avec deux boucles imbriques. Ajoutez :

// on la dcoupe !
for(int x = 0;x < nombreDeCarreX;x++) {
for(int y = 0;y < nombreDeCarreY;y++) {
// cration dune nouvelle Frame (la taille du petit carr)
CGRect frame = CGRectMake((imageSize.width / nombreDeCarreX) * x,
(imageSize.height / nombreDeCarreY) * y, imageSize.width /
nombreDeCarreX, imageSize.height / nombreDeCarreY);
//on cre un layer pour ce petit carr
CALayer *layer = [CALayer layer];
layer.frame = frame;
// dnition de laction (lanimation)
layer.actions = [NSDictionary dictionaryWithObject:[self
animationForX:x Y:y imageSize:imageSize] forKey:@opacity];
// on cre une mini image pour ce petit carr
CGImageRef subimage = CGImageCreateWithImageInRect(imageRef, frame);
layer.contents = (id)subimage;
CFRelease(subimage);
[layers addObject:layer];
}
}
Enfin, on ajoute tous ces petits carrs en tant que sous-layers de notre imageLayer :

// on ajoute les petits layers crs notre imageLayer


for(CALayer *layer in layers) {
[imageLayer addSublayer:layer];
layer.opacity = 0.0f;
}
Cela a pour effet de reconstituer notre image. Rappelez-vous, chaque sous-layer a comme content la
mini-image (subimage) dcoupe spcialement. Il faut donc enlever limage du layer principal sinon
limage sera en double et lon ne verra pas lanimation !

// maintenant que lon a reconstitu limage avec nos petits carrs, on supprime
le contenu (limage originale) de imageLayer
imageLayer.contents = nil;
Si vous avez t attentif, une nouvelle mthode a fait son apparition : animationForX:Y:imageSize:
Elle va nous servir pour crer lanimation. En fait, chaque petit carr dcoup aura sa propre animation tant donn que chaque carr est un CALayer. Cest l toute la puissance !

26

Core Animation

01_BlocN_iPhone.indd 237

237

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

238

Core Animation

Lanimation du carr en compte en fait deux : une qui va changer la transparence de chaque petit
carr, et une autre qui va le dplacer. On cre donc un groupe danimations (CAAnimationGroup) :

// Cre une animation pour chaque petit carr


- (CAAnimation *)animationForX:(NSInteger)x Y:(NSInteger)y imageSize:(CGSize)size {
// On retourne un group danimation qui comporte lanimation de lopacit
(transparence) et lanimation de la position (dplacement)
CAAnimationGroup *group = [CAAnimationGroup animation];
group.delegate = self;
group.duration = 2.0f;
// animation pour la transparence
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@opacity];
opacity.fromValue = [NSNumber numberWithDouble:1.0f]; // transparence de dpart
opacity.toValue = [NSNumber numberWithDouble:0.0f]; // transparence de n
//animation du dplacement
CABasicAnimation *position =
[CABasicAnimation animationWithKeyPath:@position];
position.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseIn];
CGPoint dest = [self randomDestinationX:x Y:y imageSize:size];
// on choisit alatoirement une position
position.toValue = [NSValue valueWithCGPoint:dest];
group.animations = [NSArray arrayWithObjects:opacity, position, nil];
// On cre un group danimation
return group;
}
Un petit schma est ncessaire pour rsumer la situation (voir Figure 26.2) :
Opacit
CALayer

Groupe
danimation
Position
Opacit

CALayer

Groupe
danimation
Position

Figure 26.2 : Schma


explicatif du dcoupage.

imageLayer

01_BlocN_iPhone.indd 238

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Encore une fois, une nouvelle mthode est apparue : randomDestinationX:Y:imageSize:... Elle va
renvoyer un point (CGPoint) qui sera la destination alatoire de notre carr :

// Choisir une destination alatoire pour le carr


- (CGPoint)randomDestinationX:(CGFloat)x Y:(CGFloat)y imageSize:(CGSize)size {
CGPoint destination;
// on tire au hasard une valeur (soit 0 soit 1)
int sensX = random()%2;
// Si cest 0, alors le sens sera -1 sinon ce sera 1
sensX = sensX == 0 ? -1 : 1;
int sensY = random()%2;
sensY = sensY == 0 ? -1 : 1;
// on tire au sort une destination
destination.x = (CGFloat)sensX * 50.0f * ((CGFloat)(random() % 10000)) / 2000.0f;
destination.y = (CGFloat)sensY * 50.0f * ((CGFloat)(random() % 10000)) / 2000.0f;
return destination;
}
Voil, cest maintenant fini ! Vous navez plus qu compiler et lancer votre application pour apprcier
le rsultat. Notez que si vous avez des warnings de compilation, vous devez vous souvenir des rgles
et ordres de lecture des fichiers lors de la compilation (nous ne dclarons pas toutes les mthodes
dans le header).
Voici donc larchitecture globale de votre .m obtenir :

#import Layers.h
#import <QuartzCore/QuartzCore.h>
//Dnition des variables
#dene
#dene
#dene
#dene
#dene
#dene

LargeurMax 300.0f
HauteurMax 300.0f
XPosition 10.0f
YPosition 20.0f
nombreDeCarreX 10.0f
nombreDeCarreY 10.0f

@implementation Layers
@synthesize imageLayer;
- (void)loadView {
...
}
// Appel lorsque lanimation se termine

26

Core Animation

01_BlocN_iPhone.indd 239

239

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

26

Core Animation

240

- (void)animationDidStop:(CAAnimation *)theAnimation nished:(BOOL)ag {


...
}
// Choisir une destination alatoire pour le carr
- (CGPoint)randomDestinationX:(CGFloat)x Y:(CGFloat)y imageSize:(CGSize)size {
...
}
// Cre une animation pour chaque petit carr
- (CAAnimation *)animationForX:(NSInteger)x Y:(NSInteger)y
...
}
// L o tout commence...
- (void)pop:(id)sender {
...
}
// mthode pour redimensionner limage et la convertir en CGImageRef
(CGImageRef)scaleAndCropImage:(UIImage *)fullImage {
...
}
- (void)didReceiveMemoryWarning {
...
}
- (void)dealloc {
[imageLayer release];
CGImageRelease(imageRef);
[super dealloc];
}
@end

01_BlocN_iPhone.indd 240

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

27

Passer des variables un serveur (GET/POST)


27

Passer des variables un serveur (GET/POST)

Les applications iPhone font trs souvent appel ce que lon nomme des webservices,
cest--dire des applications situes sur des serveurs distants (voir Fiche 34 pour lutilisation du JSON). Nous allons aborder cette problmatique par trois notions importantes :

les mthodes de classe ;


la notion de connexion Synchrone et Asynchrone ;
les requtes HTTP utilisant les mthodes POST ou GET.
Commencez par crer un nouveau projet avec la vue associe : File > New Project > View-based
Application. Vous pouvez nommer votre projet WebService pour respecter le mme nommage que
celui de louvrage.
Nous allons, pour ce projet, nous contenter dune application compatible iPhone.

MTHODE

DE CLASSE

Les mthodes de classe crent des petites fonctionnalits facilement rutilisables et constituent donc
des outils prcieux au quotidien pour un dveloppeur. Analysons tout cela avec un exemple concret.
Commencez par crer un dossier (Group) iPuP (Ctrl+Clic sur votre projet > Add > New Group).
Ensuite, ajoutez une classe CiPuP (.h et .m) dans ce dossier (Ctrl+Clic > Add > New File > ObjectiveC class subclass of NSObject).
Puis ordonnez vos fichiers pour aboutir la Figure 27.1.

Figure 27.1 : Organisation initiale


du Projet.

Implmentons maintenant une mthode de classe dans CiPuP, fichier qui dailleurs ne comprendra que
des mthodes de classe. Cette mthode permettra de faire apparatre une UIAlertView prsentant
un message.

CiPuP.h
#import <Foundation/Foundation.h>
@interface CiPuP : NSObject
+(void) afcherAlerte:(NSString *)string;
@end

27

Passer des variables un serveur (GET/POST)

01_BlocN_iPhone.indd 241

241

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

27

242

Passer des variables un serveur (GET/POST)

CiPuP.h
#import CiPuP.h
@implementation CiPuP
//La mthode de classe se distingue pas le + qui la prcde (en lieu et place du -)
+ (void) afcherAlerte:(NSString *)string{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@Alerte
message:string delegate:self cancelButtonTitle:@ok otherButtonTitles:nil];
[alert show];
[alert release];
}
@end
Voyons maintenant comment utiliser cette mthode :
Dans WebServiceViewController.m, commenons par ajouter limport :

#import CiPuP.h
@implementation WebServiceViewController
Puis dans le viewDidLoad,

- (void)viewDidLoad {
[super viewDidLoad];
[CiPuP afcherAlerte:@Ma super mthode];
}
ce stade, en compilant votre projet et lanant lapplication, vous devriez voir apparatre une alerte
avec votre message.

VRIFIER

LA DISPONIBILIT DU RSEAU

Vous allez maintenant comprendre pourquoi nous abordons ce stade du livre les mthodes de
classe.
En effet, sil y a une chose retenir pour quune application ne soit pas refuse par Apple au moment
de la validation, cest de toujours vrifier que la connexion Internet est disponible avant deffectuer
une requte synchrone sur un serveur distant.
Implmentons ainsi une mthode de classe vrifiant cette disponibilit et dont lappel sera simplifi.
Modifions le CiPuP.h ainsi :

#import <Foundation/Foundation.h>
#import <netinet/in.h>
#import <SystemConguration/SystemConguration.h>
@interface CiPuP : NSObject
+(BOOL) reseauDisponible;
+(void) afcherAlerte:(NSString *)string;
@end

01_BlocN_iPhone.indd 242

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Info
Il ne faut pas oublier dajouter le framework SystemConfiguration votre projet.

+ (BOOL)reseauDisponible {
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
SCNetworkReachabilityRef defaultRouteReachability =
SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);

SCNetworkReachabilityFlags ags;
BOOL didRetrieveFlags =
SCNetworkReachabilityGetFlags(defaultRouteReachability, &ags);

CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
return NO;
BOOL isReachable = ags & kSCNetworkFlagsReachable;
BOOL needsConnection = ags & kSCNetworkFlagsConnectionRequired;
return (isReachable && !needsConnection) ? YES : NO;
}

Info
Le code de la mthode reseauDisponible est extrait de lexemple Reachability propos par Apple.
Vous tes invit vous y rfrer pour approfondir les notions abordes dans cette fiche.
Nous aurons donc dsormais la possibilit de tester trs simplement si liPhone (ou liPod) dispose
dune connexion Internet :

if(![CiPuP reseauDisponible]){
[CiPuP showAlert:@pas de rseau disponible];
}
else {
//utilisation du webservice
}

27

Passer des variables un serveur (GET/POST)

01_BlocN_iPhone.indd 243

243

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

27

244

Passer des variables un serveur (GET/POST)

RALISATION

DE LINTERFACE GRAPHIQUE

Linterface graphique se ralise grce Interface Builder. Dans la classe WebServiceViewController,


ajoutez deux UISegmentedControl, un UILabel et un UIButton.

Figure 27.2 : Interface graphique


raliser.

Passons limplmentation du contrleur de vue.

#import <UIKit/UIKit.h>
@interface WebServiceViewController : UIViewController {
IBOutlet UISegmentedControl *getPost;
IBOutlet UISegmentedControl *synchAsynch;
IBOutlet UILabel *label;
}
-(IBAction)boutonRequete;
@end
Dans le .m :

-(IBAction)boutonRequete{
//Cas 1 : Segment 1 sur GET et Segment 2 sur Synchrone
if(getPost.selectedSegmentIndex == 0 && synchAsynch.selectedSegmentIndex == 0){
label.text = @cas 1 ;
}
//Cas 2 : Segment 1 sur POST et Segment 2 sur Synchrone
else if(getPost.selectedSegmentIndex == 1 && synchAsynch.selectedSegmentIndex == 0){
label.text = @cas 2 ;
}
//Cas 3 : Segment 1 sur GET et Segment 2 sur Asynchrone

01_BlocN_iPhone.indd 244

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

else if(getPost.selectedSegmentIndex == 0 && synchAsynch.selectedSegmentIndex == 1){


label.text = @cas 3 ;
}
//Cas 4 : Segment 1 sur POST et Segment 2 sur Asynchrone
else {
label.text = @cas 3
}
}
Les deux UISegmentedControl permettent de choisir une des quatre mthodes dinterrogation du
webservice. Le bouton, quant lui, vous permettra de lancer la requte.
Faites bien attention de relier correctement chacun des UISegmentedControl, le UILabel sans oublier
de connecter lIBAction au UIButton.
Vrifiez ensuite que cela fonctionne correctement avant de continuer.

LE

WEBSERVICE VOTRE DISPOSITION

Pour ce tutoriel, je vous propose un webservice ralis par mes soins qui est volontairement trs
simple. En fait il va simplement vous renvoyer le contenu de la variable parametre que vous allez lui
envoyer. Il fonctionne aussi bien en GET quen POST, et il est disponible cette adresse : http://www.
ipup.fr/livre/getPost
Ralis en PHP, ce service aurait tout aussi bien pu ltre en Java, en Ruby ou avec tout autre langage
compatible avec le protocole http.
Ce service est vraiment simpliste :

<?php
echo $_POST[parametre];
echo $_GET[parametre];
?>
Lide est juste de rcuprer la variable parametre transmise en GET ou en POST et de la renvoyer.
Tout lintrt est maintenant de russir transmettre notre serveur cette variable avec des informations lintrieur puis den rcuprer la rponse.

SYNCHRONE /A SYNCHRONE
Il nest pas toujours simple pour les dbutants de faire la distinction entre une mthode Synchrone et
une mthode Asynchrone.
Commenons par regarder comment se prsente en code chacune des deux mthodes :

//Synchrone :
-(void)maMethode{
[self actionUn] ;
[self actionDeux] ;
[self actionTrois] ;
}

27

Passer des variables un serveur (GET/POST)

01_BlocN_iPhone.indd 245

245

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

27

246

Passer des variables un serveur (GET/POST)

Ici on appelle actionUn qui bloque le thread principal, jusqu la fin de son excution, puis actionDeux
qui, son tour, bloque le thread principal et ainsi de suite.

//ASynchrone :
-(void)maMethode{
[self actionUnWithDelegate:self] ;
}
-(void)actionUnTerminee{
[self actionDeuxWithDelegate:self] ;
}
-(void)actionDeuxTerminee{
[self actionTroisWithDelegate:self] ;
}
-(void)actionTroisTerminee{
//cest ni
}
La grande diffrence en asynchrone est que lon ne bloque pas le thread principal, une mthode dlgue est appele quand la mthode daction est termine.
La technique de programmation est diffrente et peut paratre un peu plus lourde, mais elle est finalement trs pratique. En effet, dans notre cas, nos mthodes ralisent une requte vers un serveur
dont on ne peut pas matriser la vitesse dexcution qui dpend de la qualit du signal ainsi que de la
performance du serveur.
lusage, pour raliser des connexions en synchrone, il serait ncessaire de dtacher un thread pour
raliser la requte afin dviter le blocage de lapplication pendant lexcution de la mthode. Le
problme est qu ce stade de votre apprentissage, la gestion des threads est encore assez complique
mettre en place.
Les mthodes asynchrones vitent deux choses : de paralyser le thread principal et davoir raliser
une gestion difficile (manuelle) des threads.

Astuce
Je vous conseille le plus possible de ne pas grer les threads vous-mme car cela conduit trs
rapidement des applications instables, ou une ralisation trs lourde.

GET/POST
Sans entrer trop dans les dtails (si cela vous intresse il faut vous rfrer aux normes du protocole
http), on peut dire que la distinction principale entre POST et GET est le moyen de passer linformation au serveur.
Pour passer une variable en GET cest trs simple puisquon modifie simplement lURL. Dans notre
cas si on veut passer la variable maVariable http://monsite.com il suffit de faire une requte lURL :
http://monsite.com?maVariable=maValeur
Pour passer plusieurs variables :
http://monsite.com?maVariable1=maValeur1&maVariable2=maValeur2

01_BlocN_iPhone.indd 246

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Le principal problme et que lon ne peut pas passer tous les caractres dans une URL (il faut
respecter la norme RFC 2396). Il est possible dutiliser la mthode stringByAddingPercentEscapesUsingEncoding: de la classe NSString pour remplacer les espaces par %20 par exemple, mais cela
devient trs vite lourd.
Pour passer une variable en POST, il est ncessaire dcrire dans le corps de la requte. Opration un
peu plus complique, mais qui prsente lavantage de nous laisser beaucoup plus libre quant aux informations que lon souhaite passer.
Ces deux mises au point faites, nous allons mettre tout cela en uvre en modifiant le code de notre
contrleur.

WebServiceViewController.h
#import <UIKit/UIKit.h>
@interface WebServiceViewController : UIViewController {
IBOutlet UISegmentedControl *getPost;
IBOutlet UISegmentedControl *synchAsynch;
IBOutlet UILabel *label;
NSURLConnection
*getConnection;
NSMutableData
*getData;
NSURLConnection
*postConnection;
NSMutableData
*postData;
}
-(IBAction)boutonRequete;
-(void)actualiserLabel:(NSData*)data;
-(void)getSynch;
-(void)postSynch;
-(void)getAsynch;
-(void)postAsynch;
@end
WebServiceViewController.m
-(IBAction)boutonRequete{
//Cas 1 : Segment 1 sur GET ET Segment 2 sur Synchrone
if(getPost.selectedSegmentIndex == 0 && synchAsynch.selectedSegmentIndex == 0){
[self getSynch];
}
//Cas 2 : Segment 1 sur POST ET Segment 2 sur Synchrone
else if(getPost.selectedSegmentIndex == 1 && synchAsynch.selectedSegmentIndex == 0){
[self postSynch];
}
//Cas 3 : Segment 1 sur GET ET Segment 2 sur Asynchrone
else if(getPost.selectedSegmentIndex == 0 && synchAsynch.selectedSegmentIndex == 1){
[self getAsynch];

27

Passer des variables un serveur (GET/POST)

01_BlocN_iPhone.indd 247

247

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

27

248

Passer des variables un serveur (GET/POST)

}
//Cas 4 : Segment 1 sur POST ET Segment 2 sur Asynchrone
else {
[self postAsynch];
}
}
//mthode pour afcher une NSData dans notre label
-(void)actualiserLabel:(NSData*)data{
NSString *message = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
label.text = message;
[message release];
}
Passons maintenant aux quatre mthodes permettant de faire une requte vers un serveur web que
nous allons dtailler.

GET SYNCHRONE
Mthode pour envoyer une variable GET en synchrone :

-(void)getSynch{
//Vrication de la disponibilit du rseau
if(![CiPuP reseauDisponible]){
[CiPuP showAlert:@pas de rseau disponible];
}
else {
//On passe le paramtre dans lurl => mthode GET
NSURL *url = [[NSURL alloc] initWithString:@http://www.ipup.fr/livre/
getPost?parametre=get_synch];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[url release];
NSURLResponse *reponse = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&reponse error:&error];
//lapplication va bloquer ici le temps de faire la requte.
[request release];
[self actualiserLabel:data];
if(error){
//On afche la description de lerreur
[CiPuP showAlert:[error localizedDescription]];
}
}
}

01_BlocN_iPhone.indd 248

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

POST SYNCHRONE
-(void)postSynch{
//Vrication de la disponibilit du rseau
if(![CiPuP reseauDisponible]){
[CiPuP showAlert:@pas de rseau disponible];
}
else {
NSURL *url = [NSURL URLWithString:@http://www.ipup.fr/livre/getPost];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//On passe le paramtre dans le corps de la requte => mthode POST
[request setHTTPMethod:@POST];
[request setHTTPBody:[@parametre=post_synch
dataUsingEncoding:NSUTF8StringEncoding]];
NSURLResponse *reponse = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&reponse error:&error];
//lapplication va bloquer ici le temps de faire la requte.
[self actualiserLabel:data];
if(error){
//On afche la description de lerreur
[CiPuP showAlert:[error localizedDescription]];
}
}
}

GET A SYNCHRONE
-(void)getAsynch{
//On passe le paramtre dans lurl => mthode GET
NSURL *url = [NSURL URLWithString:@http://www.ipup.fr/livre/
getPost?parametre=get_a_synch];
NSURLRequest* request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:40.0];
getConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
getData = [[NSMutableData data] retain];
[getConnection release];
}

27

Passer des variables un serveur (GET/POST)

01_BlocN_iPhone.indd 249

249

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

27

250

Passer des variables un serveur (GET/POST)

POST A SYNCHRONE
-(void)postAsynch{
NSURL *url = [NSURL URLWithString:@http://www.ipup.fr/livre/getPost];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//On passe le paramtre dans le corps de la requte => mthode POST
[request setHTTPMethod:@POST];
[request setHTTPBody:[@parametre=post_a_synch
dataUsingEncoding:NSUTF8StringEncoding]];
postConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
postData = [[NSMutableData data] retain];
[postConnection release];
}

DLGUE

DE MTHODE ASYNCHRONE

#pragma mark #pragma mark NSURLConnection delegate


//mthode appele de manire asynchrone lorsque la connexion reoit des donnes.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if(connection == getConnection)
{
[getData appendData:data];
}
else if(connection == postConnection)
{
[postData appendData:data];
}
}
//Mthode appele en cas derreur
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
if(connection == getConnection)
{
[getData release];
}
else if(connection == postConnection)
{
[postData release];
}
//On afche la description de lerreur
[CiPuP showAlert:[error localizedDescription]];
}

01_BlocN_iPhone.indd 250

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

//mthode appele lorsque le tlchargement est termin


- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
if(connection == getConnection){
[self actualiserLabel:getData];
[getData release];
}
else if(connection == postConnection){
[self actualiserLabel:postData];
[postData release];
}
}

CONCLUSION
On peut maintenant dire que vous matrisez les fondamentaux des requtes sur serveur, et cest
vous de vous adapter chaque situation.
Vous laurez probablement compris, la mthode privilgier, car mon sens elle est la plus performante, est la mthode POST asynchrone.
Par ailleurs, dans certains cas, il peut tre trs intressant de sous-classer NSURLConnection afin de
ladapter votre situation, en y ajoutant par exemple un tag ou une gestion du cache.
Enfin, et parce quil faut aussi profiter des bonnes ressources du Net, je vous recommande le
framework ASIHTTPRequest disponible ici : http://allseeing-i.com/ASIHTTPRequest/ qui est une
surcouche du SDK trs pratique utiliser.

27

Passer des variables un serveur (GET/POST)

01_BlocN_iPhone.indd 251

251

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

252

Utiliser le carnet dadresses


28 Utiliser le carnet dadresses

Votre iPhone possde un carnet dadresses.Vous pouvez y accder depuis votre application,
et nous allons apprendre lutiliser pour envoyer des SMS et des mails.
Nous allons raliser une application qui va ressembler la Figure 28.1. Crez un nouveau projet de
type Window-based Application, que vous nommerez Composer. Ajoutez comme notre habitude un
nouveau fichier de type UIViewController que vous nommerez RootViewController (cochez With
XIB for user interface). Je vous conseille vivement de crer vous-mme linterface graphique daprs
la Figure 28.1. Si vous ntes pas encore prt, pas de souci, tout sera expliqu par la suite ! Mais
noubliez pas quil faudra vous jeter leau un jour.

Figure 28.1 : Un aperu de la fin.

AFFICHER

LA LISTE DU CARNET DADRESSES

Vous pouvez accder au carnet dadresses de deux manires :

en utilisant un contrleur cr par Apple, ABPeoplePickerNavigationController qui vous


prsentera directement la liste de vos contacts ;

en crivant des lignes de codes pour accder la base de donnes du tlphone.


Faites trs attention cette dernire solution, car il peut sembler facile de rcolter des donnes
personnelles sans que lutilisateur ne le sache. Noubliez pas quune telle pratique est strictement
illgale. Si vous souhaitez stocker des donnes personnelles sur un serveur par exemple, il faudra le
faire de manire transparente, et le dclarer la Cnil (Commission nationale de linformatique et des
liberts) en demandant une autorisation. Par contre, vous verrez quaccder la base de donnes du
carnet dadresses permet de proposer de lautocompltion trs facilement.

01_BlocN_iPhone.indd 252

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Commenons par crer notre bouton pour afficher la vue du carnet dadresses. Ajoutez ces lignes
dans le initWithNibName:bundle: du RootViewController :

// Ajout dun bouton pour afcher le rpertoire


UIButton *buttonAddressBook = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonAddressBook setTitle:@Choisir un contact forState:UIControlStateNormal];
[buttonAddressBook addTarget:self action:@selector(chooseContact:)
forControlEvents:UIControlEventTouchUpInside];
[buttonAddressBook setFrame:CGRectMake(30, 30, 260, 30)];
[self.view addSubview:buttonAddressBook];
Avant dimplmenter la mthode chooseContact:, il faut ajouter les frameworks pour bnficier du
carnet dadresses. Ajoutez donc AddressBook et AddressBookUI votre projet. Importez-les ensuite
dans RootViewController.h :

#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
Revenez au .m, et crivez la mthode chooseContact: :

- (void) chooseContact:(id)sender {
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController
alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
}
Cette dernire mthode ne prsente rien dexceptionnel, elle va juste afficher le contrleur. Par
contre, on voit que self est delegate du picker, il faut donc crire dans le .h :

@interface RootViewController : UIViewController


<ABPeoplePickerNavigationControllerDelegate>
et implmenter les mthodes suivantes dans le .m :

#pragma mark #pragma mark ABPeoplePickerNavigationControllerDelegate


// appele lorsque lutilisateur annule
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController
*)peoplePicker 
{
// on enlve le picker afch
[self dismissModalViewControllerAnimated:YES];
}
// appele lorsque le picker choisit un contact et demande si il peut continuer

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 253

253

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

254

Utiliser le carnet dadresses

(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person 
{
return YES;
}
// appel lorsque lutilisateur choisit une proprit dun contact
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property identier:(ABMultiValueIdentier)identier 
{
return YES;
}
Attardons-nous quelques instants sur ces mthodes qui sont requises toutes les trois.
La mthode ligne  est appele lorsque lutilisateur appuie sur le bouton Annuler. Le comportement
basique est denlever le contrleur de lcran.
La mthode la ligne , est appele quand lutilisateur choisit un contact. Cette dernire demande
si le picker peut continuer, cest--dire si elle peut afficher dans une nouvelle vue les informations
du contact (Nom, prnom, adresse, numros de tlphones...).
Enfin, la mthode ligne  demande si le picker peut continuer aprs que lutilisateur a appuy
sur une des proprits du contact. En retournant YES comme au-dessus, le comportement sera le
suivant : lutilisateur slectionne un contact, puis peut choisir une proprit. Sil appuie sur un
numro de tlphone, liPhone appellera ce numro, sil appuie sur ladresse mail, cela lancera
lapplication mail.
Maintenant, si vous souhaitez utiliser le carnet dadresses dans votre application pour demander par
exemple lutilisateur de slectionner un numro de tlphone, il faudra laisser  et  comme
ci-dessus et crire dans la mthode  :

// appel lorsque lutilisateur choisit une proprit dun contact


- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property identier:(ABMultiValueIdentier)identier
{
if(property == kABPersonPhoneProperty)
{
// on rcupre le ou les numro(s) de tlphone
aBMultiValueRef phoneNumbers =
ABRecordCopyValue(person, kABPersonPhoneProperty);
// on les place dans un tableau
NSArray *theArray =
[(id)ABMultiValueCopyArrayOfAllValues(phoneNumbers) autorelease];
// on afche le numro de tlphone choisit en fonction de lidentier

01_BlocN_iPhone.indd 254

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSLog(@numro de tlphone %@, [theArray


objectAtIndex:ABMultiValueGetIndexForIdentier(phoneNumbers, identier)]);

[self dismissModalViewControllerAnimated:YES];
}
return NO;
}
Dans notre cas, nous allons demander lutilisateur de choisir un contact pour rcuprer :

son nom ;
son prnom ;
un numro de tlphone ;
une adresse e-mail.

RCUPRER

LES PROPRITS DUN CONTACT

Crons tout dabord nos quatre labels pour afficher ces informations. Pour cela, crivez dans .h :

UILabel *labelName, *labelFirstName, *labelPhoneNumber, *labelMailAddress;


Puis, dans le initWithNibName:bundle:, crivez :

// ajout du label pour le nom


labelName = [[UILabel alloc] initWithFrame:CGRectMake(30, 80, 110, 30)];
[labelName setText:@Nom];
labelName.adjustsFontSizeToFitWidth = YES;
[self.view addSubview:labelName];
// label pour le prnom
labelFirstName = [[UILabel alloc] initWithFrame:CGRectMake(180, 80, 110, 30)];
[labelFirstName setText:@Prnom];
labelFirstName.adjustsFontSizeToFitWidth = YES;
[self.view addSubview:labelFirstName];
// label pour le numro du destinataire
labelPhoneNumber = [[UILabel alloc] initWithFrame:CGRectMake(30, 130, 110, 30)];
[labelPhoneNumber setText:@Numro];
labelPhoneNumber.adjustsFontSizeToFitWidth = YES;
[self.view addSubview:labelPhoneNumber];
// label pour le numro du destinataire
labelMailAddress = [[UILabel alloc] initWithFrame:CGRectMake(180, 130, 110, 30)];
[labelMailAddress setText:@Adresse mail];
labelMailAddress.adjustsFontSizeToFitWidth = YES;
[self.view addSubview:labelMailAddress];

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 255

255

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

256

Utiliser le carnet dadresses

Notez le recours adjustsFontSizeToFitWidth qui va permettre dajuster la taille de la police pour


afficher entirement les donnes (il y a tout de mme une taille de police minimale).
Ensuite, nous allons implmenter les mthodes delegate de ABPeoplePickerNavigationControllerDelegate. Nous ne permettrons pas laffichage de la vue dtaille dun contact, car nous souhaitons
slectionner quatre de ses proprits en mme temps :

#pragma mark #pragma mark ABPeoplePickerNavigationControllerDelegate


// appele lorsque lutilisateur annule
- (void)
peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController
*)peoplePicker
{
// on enlve le picker afch
[self dismissModalViewControllerAnimated:YES];
}
// appele lorsque le picker choisit un contact et demande si il peut continuer
(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
[self dismissModalViewControllerAnimated:YES];
return NO;
}
// appel lorsque lutilisateur choisit une proprit dun contact
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property identier:(ABMultiValueIdentier)identier
{
[self dismissModalViewControllerAnimated:YES];
return NO;
}
Affichons maintenant le nom ainsi que le prnom du contact choisi :

// appele lorsque le picker choisit un contact et demande si il peut continuer


(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
// on rcupre le prnom
NSString* name = (NSString *)ABRecordCopyValue(person,
kABPersonFirstNameProperty); 
labelFirstName.text = name;
[name release];

01_BlocN_iPhone.indd 256

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// on rcupre le nom
name = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
labelName.text = name;
[name release];
[self dismissModalViewControllerAnimated:YES];
return NO;
}
Jusque-l, rien de bien sorcier. Un contact ne pouvant avoir quun nom et quun prnom, nous copions
la proprit prnom (kABPersonFirstNameProperty), par exemple, dans un objet NSString partir du
contact slectionn person ligne .
Par contre, nous souhaitons maintenant permettre de choisir une adresse mail. Or, un contact peut
en avoir plusieurs...Toujours dans la mme mthode, la suite de la rcupration du nom, ajoutez ces
deux lignes :

// on rcupre le ou les e-mail(s)


ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
// on les place dans un tableau
NSArray *theArray = [(id)ABMultiValueCopyArrayOfAllValues(emails) autorelease];
Voil, on a un tableau contenant toutes les adresses mails de notre contact. Pour demander lutilisateur de choisir, nous avons besoin dune UIActionSheet dont chaque bouton prsentera une adresse
mail, dans le cas o le contact slectionn a au moins deux adresses mails.
Ajoutez la suite :

UIActionSheet *actionSheet = nil;


if ([theArray count] == 0) {
// pas dadresse mail
labelMailAddress.text = @Pas de mail;
}
else {
if ([theArray count] == 1) {
// une seule adresse
labelMailAddress.text = [theArray objectAtIndex:0];
}
else {
// on demande lutilisateur de choisir
actionSheet = [[UIActionSheet alloc] initWithTitle:@Choisir un email
delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil
otherButtonTitles:nil];
// ajouter un bouton pour chaque e-mail
for (NSString *email in theArray)
[actionSheet addButtonWithTitle:email];
[actionSheet showInView:self.view];
}

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 257

257

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

258

Utiliser le carnet dadresses

}
// on libre
[(id)emails release];
// on release laction sheet
if (actionSheet) {
[actionSheet release];
}
Comme vous le voyez, self est delegate de laction sheet, et doit tre conforme au protocole UIActionSheetDelegate :

@interface RootViewController : UIViewController


<ABPeoplePickerNavigationControllerDelegate, UIActionSheetDelegate>
Ensuite, il faut implmenter la mthode appele lorsque lutilisateur slectionne un bouton de cette
liste, puis afficher ladresse mail slectionne :

#pragma mark #pragma mark actionSheet delegate


- (void)actionSheet:(UIActionSheet *)actionSheet
willDismissWithButtonIndex:(NSInteger)buttonIndex
{
[labelMailAddress setText:[actionSheet buttonTitleAtIndex:buttonIndex]];
}
Facile non ? Ajoutons maintenant le choix du numro de tlphone la suite. Pour cela, il va falloir
jouer un petit peu des coudes... En effet, dans le cas le plus dfavorable, o le contact slectionn
plusieurs adresses mails ainsi que plusieurs numros de tlphone, il faudra prsenter deux objets
UIActionSheet la suite. Or, cette vue de slection saffiche de manire modale dans la vue, cest donc
la premire action sheet affiche qui reoit les vnements. Dans le cas o vous ajoutez immdiatement une autre action sheet par-dessus, elle saffichera par-dessus la premire, mais ce sera celle du
mail qui rpondra la slection... a ne marche donc pas... Si le contact a plusieurs adresses mails et
plusieurs numros, il faut sauvegarder le tableau de numros de tlphone pour lafficher ensuite.
Pour cela, on crit dans le .h :

ABMultiValueRef multiValueToDisplay;
et dans le .m (en gras),

// appele lorsque le picker choisit un contact et demande si il peut continuer


- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController
*)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
// on rcupre le prnom
NSString* name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
labelFirstName.text = name;
[name release];
// on rcupre le nom
name = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);

01_BlocN_iPhone.indd 258

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

labelName.text = name;
[name release];
// on rcupre le ou les e-mail(s)
aBMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
// on les place dans un tableau
NSArray *theArray = [(id)ABMultiValueCopyArrayOfAllValues(emails) autorelease];
UIActionSheet *actionSheet = nil;
if ([theArray count] == 0) {
// pas dadresse mail
labelMailAddress.text = @Pas de mail;
}
else {
if ([theArray count] == 1) {
// une seule adresse
labelMailAddress.text = [theArray objectAtIndex:0];
}
else {
// on demande lutilisateur de choisir
actionSheet = [[UIActionSheet alloc] initWithTitle:@Choisir un email
delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil
otherButtonTitles:nil];
// ajouter un bouton pour chaque e-mail
for (NSString *email in theArray)
[actionSheet addButtonWithTitle:email];
[actionSheet showInView:self.view];
}
}
// on libre
[(id)emails release];
// on rcupre le ou les numro(s) de tlphone
aBMultiValueRef phoneNumbers =
ABRecordCopyValue(person, kABPersonPhoneProperty);
// on les place dans un tableau
theArray = [(id)ABMultiValueCopyArrayOfAllValues(phoneNumbers) autorelease];
if ([theArray count] == 0) {
// pas de tlphone
labelPhoneNumber.text = @Pas de tlphone;
}
else {
if ([theArray count] == 1) {

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 259

259

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

260

Utiliser le carnet dadresses

// un seul numro
labelPhoneNumber.text = [theArray objectAtIndex:0];
}
else {
if(actionSheet)
{
// on a dj une action sheet dafche donc on attend avant
dafcher la suivante
multiValueToDisplay = [(id)phoneNumbers retain];
}
else
{
// il ny avait que 0 ou 1 mail
// on demande lutilisateur de choisir
actionSheet = [[UIActionSheet alloc] initWithTitle:@Choisir un
tlphone delegate:self cancelButtonTitle:nil
destructiveButtonTitle:nil otherButtonTitles:nil];
// ajouter un bouton pour chaque numro
for (NSString *numbers in theArray)
[actionSheet addButtonWithTitle:numbers];
[actionSheet showInView:self.view];
}
}
}
[(id)phoneNumbers release];
// on release laction sheet
if (actionSheet) {
[actionSheet release];
}
[self dismissModalViewControllerAnimated:YES];
return NO;
}
Puis, dans la mthode delegate de UIActionSheetDelegate, faites ces modifications :

#pragma mark #pragma mark actionSheet delegate


- (void)actionSheet:(UIActionSheet *)actionSheet
willDismissWithButtonIndex:(NSInteger)buttonIndex
{
if ([actionSheet.title isEqualToString:@Choisir un email]) 
{
[labelMailAddress setText:[actionSheet buttonTitleAtIndex:buttonIndex]];
if (multiValueToDisplay) {

01_BlocN_iPhone.indd 260

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// on va maintenant demander de choisir parmi les numros de


tlphones disponibles

NSArray *theArray =
[(id)ABMultiValueCopyArrayOfAllValues(multiValueToDisplay) autorelease];

UIActionSheet *actionSheet = [[UIActionSheet alloc]


initWithTitle:@Choisir un tlphone delegate:self
cancelButtonTitle:nil destructiveButtonTitle:nil
otherButtonTitles:nil];

// ajouter un bouton pour chaque numro


for (NSString *numbers in theArray)
[actionSheet addButtonWithTitle:numbers];
[actionSheet showInView:self.view];
[actionSheet release];
[(id)multiValueToDisplay release];
}
}
else { 
[labelPhoneNumber setText:[actionSheet buttonTitleAtIndex:buttonIndex]];
}
}
Ici, on regarde le titre de laction sheet concerne (nous pourrions galement utiliser la proprit
tag). Si laction sheet prsente comme titre Choisir un email ligne , alors on affiche ladresse mail
choisie. Ensuite on regarde sil y a plusieurs numros de tlphone afficher en testant la valeur de
multiValueToDisplay qui ne doit pas pointer vers nil ligne . Cela revient tester lexistence en
mmoire de multiValueToDisplay. Dans le cas o laction sheet prsente les numros (ligne ), on
affiche le numro choisi.

A JOUTER

LES LMENTS DINTERFACE POUR CRIRE UN MESSAGE

Avant denvoyer un mail ou un SMS, nous allons crer deux champs de texte pour saisir lobjet et le
corps du message et deux boutons pour envoyer le SMS ou le mail.

Info
Lorsque vous envoyez un SMS, vous ne pouvez spcifier dobjet. Ce champ de texte concerne donc
uniquement lenvoi de-mail.
Avant de permettre lutilisateur de lancer une fonctionnalit, il faut toujours vrifier si elle est disponible sur lappareil. On a recours aux mthodes canSendText pour lenvoi de SMS et canSendMail
pour lenvoi de mails.

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 261

261

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

262

Utiliser le carnet dadresses

Ajoutez ces lignes dans le initWithNibName:bundle: :

// textField pour lobjet (dans le cas dun mail)


textFieldSubject = [[UITextField alloc] initWithFrame:CGRectMake(30, 180, 260, 30)];
[textFieldSubject setBorderStyle:UITextBorderStyleRoundedRect];[textFieldSubject
setPlaceholder:@Objet];
textFieldSubject.delegate = self;
[self.view addSubview:textFieldSubject];
// textField pour le contenu
textFieldBody = [[UITextField alloc] initWithFrame:CGRectMake(30, 230, 260, 100)];
[textFieldBody setBorderStyle:UITextBorderStyleRoundedRect];
[textFieldBody setPlaceholder:@Message];
textFieldBody.delegate = self;
[self.view addSubview:textFieldBody];
// si lappareil peut envoyer des sms, on ajoute le bouton
if ([MFMessageComposeViewController canSendText]) {
// bouton envoi SMS
UIButton *buttonSendSMS = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonSendSMS setTitle:@SMS forState:UIControlStateNormal];
[buttonSendSMS addTarget:self action:@selector(sendSMS:)
forControlEvents:UIControlEventTouchUpInside];
[buttonSendSMS setFrame:CGRectMake(30, 350, 110, 30)];
[self.view addSubview:buttonSendSMS];
}
// si lappareil peut envoyer des mails, on ajoute le bouton
if ([MFMailComposeViewController canSendMail]) {
// bouton envoi Mail
UIButton *buttonSendMail = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonSendMail setTitle:@Mail forState:UIControlStateNormal];
[buttonSendMail addTarget:self action:@selector(sendMail:)
forControlEvents:UIControlEventTouchUpInside];
[buttonSendMail setFrame:CGRectMake(180, 350, 110, 30)];
[self.view addSubview:buttonSendMail];
}
Il vous faudra donc ajouter le framework MessageUI et limporter dans le .h :

#import <MessageUI/MessageUI.h>

01_BlocN_iPhone.indd 262

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Il faut galement dclarer les deux champs de texte :

UITextField *textFieldBody, *textFieldSubject;


De plus, nous souhaitons que la classe RootViewController se conforme au protocole UITextFieldDelegate pour cacher le clavier lorsque lutilisateur appuie sur le bouton Retour.
@interface RootViewController :
UIViewController <ABPeoplePickerNavigationControllerDelegate,
UIActionSheetDelegate, UITextFieldDelegate>
crire dans le .m :

#pragma mark #pragma mark text eld delegate methods


- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// cacher le clavier
[textField resignFirstResponder];
return YES;
}
Si vous lancez votre application, vous remarquerez quen cliquant sur le champ de texte o rentrer le
corps de votre message, le clavier vient sy superposer pour le cacher... Nous allons donc dplacer la
vue (la remonter) en mme temps que lapparition du clavier :

// appele lorsque lutilisateur va diter


- (void)textFieldDidBeginEditing:(UITextField *)textField {
// on anime le retour de la vue
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
// on dplace la vue de 120 pixels vers le haut
self.view.transform = CGAfneTransformMakeTranslation(0.0, -120.0);
[UIView commitAnimations];
}
// appele lorsque lutilisateur arrte ldition
- (void)textFieldDidEndEditing:(UITextField *)textField {
// on dplace la vue en mme temps que le clavier pour quil ne cache pas le
text eld
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
// on remet la vue son tat initial
self.view.transform = CGAfneTransformIdentity;
[UIView commitAnimations];
}

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 263

263

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

264

Utiliser le carnet dadresses

ENVOYER

UN

SMS

Lenvoi de SMS dans une application est une nouveaut de iOS4. Auparavant, lenvoi de SMS tait
possible en faisant quitter votre application, et vous ne pouviez passer de corps de message :

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@sms:0612345678]];

Attention
Lenvoi de SMS par votre application utilise le forfait de lutilisateur. Il devra donc payer sil ne dispose
pas dun forfait illimit.
Vous ne pouvez pas envoyer de SMS en le cachant lutilisateur et sans lui demander confirmation
travers le contrleur prsent la Figure 28.2.
Votre application ne pourra recevoir de SMS qui seront, par dfaut, affichs dans lapplication SMS
natives de votre tlphone.
Pour afficher un SMS, crivez ceci dans le .m, tout simplement :

- (void) sendSMS :(id)sender {


MFMessageComposeViewController *picker =
[[MFMessageComposeViewController alloc] init];
picker.messageComposeDelegate = self;
// on met le numro de tlphone
picker.recipients = [NSArray
arrayWithObject:labelPhoneNumber.text];
// on met le contenu du SMS
picker.body = textFieldBody.text;
// on afche la vue
[self presentModalViewController:picker
animated:YES];
[picker release];
}

Figure 28.2 : Fentre de


confirmation de lenvoi dun SMS.

Cela aura pour effet de placer le numro dans le champ recipients (destinataires), ainsi que le texte
crit dans le champ body (corps) et dafficher la vue de confirmation de lenvoi.
Il ne vous aura pas chapp que RootViewController doit se conformer au protocole MFMessageComposeViewControllerDelegate :

@interface RootViewController : UIViewController


<ABPeoplePickerNavigationControllerDelegate, UIActionSheetDelegate,
UITextFieldDelegate, MFMessageComposeViewControllerDelegate>

01_BlocN_iPhone.indd 264

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Il faut donc implmenter la mthode suivante, appele lorsque lutilisateur annule lenvoi ou appuie
sur le bouton Envoyer, et procder au retrait de la vue de lcran. Cette mthode retourne galement
le statut de lenvoi. vous davertir lutilisateur !

#pragma mark #pragma mark Message SMS delegate


- (void) messageComposeViewController:(MFMessageComposeViewController *)controller
didFinishWithResult:(MessageComposeResult)result {
switch (result) {
case MessageComposeResultSent:
NSLog(@SMS envoy);
break;
case MessageComposeResultCancelled:
NSLog(@SMS annul);
break;
case MessageComposeResultFailed:
NSLog(@SMS chec de lenvoi);
break;
default:
break;
}
[self dismissModalViewControllerAnimated:YES];
}

ENVOYER

UN E- MAIL

Envoyer un e-mail ressemble fortement lenvoi dun SMS et est rgi par les mmes rgles en ce qui
concerne la validation par lutilisateur. Ladresse e-mail denvoi est celle spcifie dans les rglages
comme tant ladresse e-mail denvoi par dfaut.
- (void) sendMail :(id)sender {
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
// on met le destinataire du mail
[picker setToRecipients:[NSArray arrayWithObject:labelMailAddress.text]];
/* on peut aussi spcier
// les gens en copie
[picker setCcRecipients:];
// les gens en copie cache
[picker setBccRecipients:];
*/

// on met le sujet du mail


[picker setSubject:textFieldSubject.text];
// on met le contenu du mail. On peut choisir de mettre du contenu html
[picker setMessageBody:textFieldBody.text isHTML:NO];
// on afche la vue

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 265

265

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

28

Utiliser le carnet dadresses

266

[self presentModalViewController:picker animated:YES];


[picker release];
}
Vous pouvez galement envoyer un mail avec un contenu HTML :

[picker setMessageBody:messageHTML isHTML:YES];


Pour finir, implmentez le protocole MFMailComposeViewControllerDelegate :

@interface RootViewController : UIViewController


<ABPeoplePickerNavigationControllerDelegate, UIActionSheetDelegate,
UITextFieldDelegate, MFMessageComposeViewControllerDelegate,
MFMailComposeViewControllerDelegate>
et :

#pragma mark #pragma mark Mail compose delegate


- (void) mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
switch (result) {
case MFMailComposeResultSent:
NSLog(@Mail envoy);
break;
case MFMailComposeResultSaved:
NSLog(@Mail sauvegard);
break;
case MFMailComposeResultFailed:
NSLog(@Mail chec de lenvoi);
break;
case MFMailComposeResultCancelled:
NSLog(@Mail annul);
break;
default:
break;
}
[self dismissModalViewControllerAnimated:YES];
}
Cette fiche est maintenant presque termine, noubliez pas le dealloc !

- (void)dealloc {
[labelPhoneNumber release];
[labelName release];
[labelFirstName release];
[labelMailAddress release];
[textFieldBody release];
[textFieldSubject release];
[super dealloc];
}

01_BlocN_iPhone.indd 266

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

DERNIRES

REMARQUES

Nous avons vu comment lire le carnet dadresses de votre appareil, mais vous pouvez galement y
ajouter des contacts, modifier ceux existants, en crer une copie... Pour cela, rfrez-vous la documentation ! En effet, couvrir tous ces aspects mriteraient un ouvrage ddi. Par contre, si vous
souhaitez rcuprer tout le carnet dadresses par le code dans un tableau :

NSMutableArray *peopleArray =
(NSMutableArray *)ABAddressBookCopyArrayOfAllPeople(ABAddressBookCreate());
// utilisation du tableau
[peopleArray release];

Info
Vous aurez certainement remarqu quil y a plus simple pour envoyer un SMS ou un mail, car les
contrleurs ddis intgrent directement le choix des destinataires... Mais cela ne prsentait aucun
intrt dans la rdaction de cette fiche...

28

Utiliser le carnet dadresses

01_BlocN_iPhone.indd 267

267

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

29

268

Accder votre calendrier


29 Accder votre calendrier

Avec iOS4, Apple autorise laccs au calendrier de votre tlphone pour le consulter et
le modifier. Regardons tout ceci de plus prs !
Pour linstant, vous ne pouvez pas crer votre propre calendrier. Les calendriers disponibles sont
ceux de lapplication Calendrier, stocks dans sa base de donnes. Par contre, il vous incombe de
construire linterface graphique pour prsenter les donnes lutilisateur. Ici, nous allons le faire trs
simplement avec une table view.

Info
Aucune interface graphique prte lemploi nexiste
pour prsenter le calendrier. Cependant, les contrleurs
ncessaires pour crer un nouvel vnement ou en
modifier un sont, eux, disponibles.
Commencez par crer un nouveau projet de type Navigation-based Application que vous nommerez EventFromCalendar. Ensuite, ajoutez les deux frameworks
EventKit et EventKitUI comme la Figure 29.1

RCUPRER

Figure 29.1 : Lajout des frameworks.

LES VNEMENTS

DU CALENDRIER
Dans cette premire partie, nous allons afficher les
vnements des calendriers en les divisant par nom de
calendriers. Chaque vnement une proprit
calendar (EKCalendar) qui lui-mme a une proprit
title (NSString). Laffichage se fera par section, comme
prsent la Figure 29.2.
Pour commencer, importer les frameworks dans le
RootViewController.h :

#import <EventKit/EventKit.h>
#import <EventKitUI/EventKitUI.h>

Figure 29.2 : Laffichage final.

01_BlocN_iPhone.indd 268

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Puis, nous allons dclarer un objet de la classe EKEventStore. Cet objet permet de faire le lien avec la
base de donnes du calendrier sur votre appareil. Dclarez galement un tableau qui contiendra les
lments afficher.

@interface RootViewController : UITableViewController {


EKEventStore *eventStore;
NSMutableArray *eventsToDisplay;
}
@property (nonatomic, retain) NSMutableArray *eventsToDisplay;
@end
Dans le .m, ajoutez la ligne en gras

@implementation RootViewController
@synthesize eventsToDisplay;
Commenons par allouer notre objet eventStore dans le viewDidLoad :

- (void)viewDidLoad {
[super viewDidLoad];
// titre du controlleur
self.title = @Calendrier;
// initialisation de levent store
eventStore = [[EKEventStore alloc] init];
}
Pour afficher les donnes, nous allons remplir un tableau, dclar dans le .h. Ajoutez cette ligne,
toujours dans le viewDidLoad :

// initialisation du tableau avec les vnements deux mois avant et un mois


aprs la date actuelle
self.eventsToDisplay = [self getEventsTodisplay];
Il faut maintenant dfinir cette nouvelle mthode getEventsToDisplay. Pour cela, ajoutez cette ligne
dans le .h :

(NSMutableArray*) getEventsTodisplay;
Implmentons cette mthode, dans le .m en commenant par dfinir un intervalle de temps dans
lequel nous allons rcuprer tous les vnements. Cet intervalle de temps commencera 2 mois avant
la date actuelle et finira 1 mois aprs.

- (NSMutableArray*) getEventsTodisplay {
// on cr les dates limites pour le tri
CFGregorianDate gregorianStartDate, gregorianEndDate;
// deux mois avant aujourdhui
CFGregorianUnits startUnits = {0, -2, 0, 0, 0, 0}; // annes, mois, jours,
heures, minutes, secondes

29

Accder votre calendrier

01_BlocN_iPhone.indd 269

269

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

29

270

Accder votre calendrier

// un mois aprs aujourdhui


CFGregorianUnits endUnits = {0, 0, 30, 0, 0, 0};
// rcuprer lheure actuelle dans la zone o vous tes placs
CFTimeZoneRef timeZone = CFTimeZoneCopySystem();
gregorianStartDate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeAddGre go
rianUnits(CFAbsoluteTimeGetCurrent(), timeZone, startUnits), timeZone);

gregorianEndDate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeAddGrego
rianUnits(CFAbsoluteTimeGetCurrent(), timeZone, endUnits), timeZone);
NSDate* startDate = [NSDate dateWithTimeIntervalSinceReferenceDate:CFGrego
rianDateGetAbsoluteTime(gregorianStartDate, timeZone)];
NSDate* endDate = [NSDate dateWithTimeIntervalSinceReferenceDate:CFGrego
rianDateGetAbsoluteTime(gregorianEndDate, timeZone)];
CFRelease(timeZone);
// lajout des mthodes se fera toujours avant la ligne suivante, ici
return nil; // pour linstant on retourne nil
}
Ensuite, il faut rcuprer dans la base de donnes les vnements qui correspondent. Regardez dans
la documentation la classe EKEvent pour voir tous ses attributs et vous les approprier.

// On cre le prdicat
NSPredicate *predicate = [eventStore
predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];
// On rcupre tous les vnements qui correspondent
NSArray *events = [eventStore eventsMatchingPredicate:predicate];
Nous pourrions nous arrter ici et afficher directement les vnements. Pour corser le tout, je vais
vous les faire afficher en utilisant une section par calendrier. Pour cela, nous allons remplir un tableau
comme la Figure 29.3.

01_BlocN_iPhone.indd 270

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

eventsArray NSMutableArray
0

dic1 NSMutableDictionary
Objet

Cl

calendarName1 NSString

kCalendarName

arrayOfEvents1 NSMutableArray

kArrayOfEvents

Event1 EKEvent
Event2 EKEvent

EventN EKEvent

dic2 NSMutableDictionary
Objet

Cl

calendarName2 NSString

kCalendarName

arrayOfEvents2 NSMutableArray

kArrayOfEvents

Figure 29.3 : La structure


du tableau.

Event1 EKEvent
Event2 EKEvent

EventN EKEvent

N-1

dicN NSMutableDictionary
Objet

Cl

calendarNameN NSString

kCalendarName

arrayOfEventsN NSMutableArray

kArrayOfEvents

Event1 EKEvent
Event2 EKEvent

EventN EKEvent

La premire chose faire est de rcuprer les noms des calendriers pour tous les vnements que
lon vient de prendre depuis la base de donnes. Pour cela, on cre un nouveau tableau en fonction
de calendar.title. En fait, events est un tableau de EKEvent, donc pour chaque objet event de cette
classe, on peut rcuprer le titre du calendrier auquel il appartient par event.calendrier.title.

29

Accder votre calendrier

01_BlocN_iPhone.indd 271

271

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

29

272

Accder votre calendrier

// on rcupre les diffrents type de calendrier


NSArray *arrayOfCalendarsWithPotentialDuplication =
[events valueForKeyPath:@calendar.title];
Il y a un risque potentiel de doublons, car valueForKeyPath: cre un tableau de la mme taille que
celui spcifi en paramtre. On les limine facilement, grce aux proprits de la classe NSSet (qui
reprsente un ensemble dobjets distincts et non organiss).

// comme il y a un risque de duplication des noms de calendriers, on utilise


NSSet qui va automatiquement enlever les doublons
NSSet *setCalendars = [NSSet setWithArray:arrayOfCalendarsWithPotentialDuplication];
// on rcupre le bon tableau sans doublons
NSArray *arrayOfCalendars = [setCalendars allObjects];
Ensuite, on construit le nouveau tableau en numrant nos diffrents noms de calendriers :

// on construit un tableau de dictionnaires contenant le nom du calendrier et un


tableau dvnements correspondants ce calendrier
NSMutableArray *arrayToReturn = [[NSMutableArray alloc] init];
// on parcourt tous les calendriers
for (NSString *calendar in arrayOfCalendars)
{
// on cherche les vnements qui ont pour titre le calendrier en cours
NSPredicate *predicateCalendar =
[NSPredicate predicateWithFormat:@calendar.title == %@, calendar];
// on cre un nouveau tableau en utilisant ce ltre
NSMutableArray *arrayOfEvents =
[[events lteredArrayUsingPredicate:predicateCalendar] mutableCopy];
// on cre le dictionnaire associ
NSMutableDictionary *dic = [NSMutableDictionary
dictionaryWithObjects:[NSArray arrayWithObjects:calendar, arrayOfEvents, nil]
forKeys:[NSArray arrayWithObjects:kCalendarName, kArrayOfEvents, nil]];
[arrayOfEvents release]; // release cause de mutableCopy
// on lajoute dans le tableau
[arrayToReturn addObject:dic];
}
Et on retourne le tableau construit, remplacez cette ligne :

return nil; // pour linstant on retourne nil


par :

// on retourne le tableau en autorelease cf Fiche mmoire


return [arrayToReturn autorelease];
}
Noubliez pas de dclarer :

#dene kCalendarName @Calendar name


#dene kArrayOfEvents @Array of events

01_BlocN_iPhone.indd 272

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

AFFICHER

LES VNEMENTS DU CALENDRIER

Pour afficher les vnements dans notre table view, il faut tout dabord dfinir le nombre de sections
qui correspond au nombre de calendriers :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [eventsToDisplay count];
}
Puis, il faut spcifier le nombre de lignes pour chaque section :

- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [[[eventsToDisplay objectAtIndex:section] objectForKey:kArrayOfEvents] count];
}
Affichons le titre de chaque section en ajoutant cette mthode :

- (NSString*)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
return [[eventsToDisplay objectAtIndex:section] objectForKey:kCalendarName];
}
Puis, affichez chaque vnement dans une cellule en modifiant le type de cellule en UITableViewCellStyleSubtitle pour faire apparatre les lments sur deux lignes :

- (UITableViewCell *)tableView:(UITableView
*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentier:CellIdentier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentier:CellIdentier] autorelease];
}
return cell;
}
Rcuprer lvnement afficher en ajoutant au-dessus de return cell; :

EKEvent *event = (EKEvent*)[[[eventsToDisplay objectAtIndex:indexPath.section]


objectForKey:kArrayOfEvents] objectAtIndex:indexPath.row];
Ensuite, formatons les dates sur le format Jour Mois Anne/Heure:Minute :

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];


dateFormatter.dateFormat = @dd MMMM YY / HH:mm;
NSString *startDate = [dateFormatter stringFromDate:event.startDate];
NSString *endDate = [dateFormatter stringFromDate:event.endDate];
[dateFormatter release];

29

Accder votre calendrier

01_BlocN_iPhone.indd 273

273

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

29

274

Accder votre calendrier

Enfin, affichons dans les labels :

cell.textLabel.text = event.title;
cell.detailTextLabel.text = [NSString stringWithFormat:@%@ > %@, startDate,
endDate];
Voil, vous devriez avoir un affichage correct !

A JOUTER

UN NOUVEL VNEMENT

Vous pouvez ajouter un nouvel vnement dans un calendrier de deux manires :

soit en le crant la main ;


soit en utilisant un contrleur tout fait : EKEventEditViewController. Vous devez passer ce
dernier lobjet de la classe EKEventStore que vous manipulez. Ensuite, si vous laissez sa proprit
event nil, il crera automatiquement un nouvel vnement, sinon, il modifiera celui pass en
paramtre.
Ajoutons un bouton + dans la barre de navigation. Dans le viewDidLoad, ajoutez :

// ajout du bouton ajouter un nouvel vnement


UIBarButtonItem *plusButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self
action:@selector(addEvent:)];
self.navigationItem.leftBarButtonItem = plusButton;
[plusButton release];
Puis implmentez la mthode addEvent; :

- (void)addEvent:(id)sender {
EKEventEditViewController* controller = [[EKEventEditViewController alloc] init];
controller.eventStore = eventStore;
controller.editViewDelegate = self;
[self presentModalViewController: controller animated:YES];
[controller release];
}
Comme vous le voyez, RootViewController est delegate de EKEventEditViewDelegate, rajoutez les
termes en gras dans le .h :

@interface RootViewController : UITableViewController <EKEventEditViewDelegate> {


Et implmentez la mthode appele lorsque le contrleur a fini (dans le .m) :

- (void)eventEditViewController:(EKEventEditViewController *)controller
didCompleteWithAction:(EKEventEditViewAction)action
{
[self dismissModalViewControllerAnimated:YES];
}

01_BlocN_iPhone.indd 274

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Info
Notez que cette mthode passe en paramtre une variable action de type EKEventEditViewAction.
Cette variable peut prendre les valeurs suivantes :
EKEventEditViewActionCanceled Lorsque lutilisateur annule lajout dun nouvel vnement.

EKEventEditViewActionSaved Lorsque lutilisateur sauvegarde ses modifications.


EKEventEditViewActionDeleted Lorsque lutilisateur supprime lvnement.
Vous pouvez rcuprer lvnement modifi, ajout, ou supprim avec controller.event.
Regardons une mthode requise demandant le calendrier ncessaire lajout de lvnement. Nous
retournerons ici le calendrier par dfaut, sachant galement que lutilisateur peut le modifier par la
suite dans la vue du contrleur.

- (EKCalendar
*)eventEditViewControllerDefaultCalendarForNewEvents:(EKEventEditViewController
*)
controller
{
return eventStore.defaultCalendarForNewEvents;
}
Lajout de nouveaux vnements est oprationnel. Problme, ils napparaissent pas... En fait, le contrleur met jour la base de donnes des calendriers. Avec le multitche, une application peut modifier
cette base de donnes, et donc rendre votre affichage obsolte. Nous allons donc ajouter un observateur qui va appeler une mthode chaque fois que la base de donnes changera, pour la mettre jour
dans votre application. Ajoutez cette ligne dans le viewDidLoad :

// ajout de lobservation du changement de la base de donnes


[[NSNoticationCenter defaultCenter] addObserver:self selector:@selector(storeChanged:)
name:EKEventStoreChangedNotication object:eventStore];
Puis, implmentez storeChanged: o nous rcuprerons la nouvelle base de donnes pour ensuite
relancer laffichage :

- (void)storeChanged:(EKEventStore*)theEventStore
{
self.eventsToDisplay = [self getEventsTodisplay];
[self.tableView reloadData];
}

Info
Pour tre dans les rgles, il ne faudrait excuter le contenu de la mthode storeChanged: que lorsque
lapplication est visible (le dsactiver quand lapplication est en fond de tche).

29

Accder votre calendrier

01_BlocN_iPhone.indd 275

275

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

29

276

Accder votre calendrier

SUPPRIMER

UN VNEMENT

Implmentons la possibilit de supprimer un vnement de la base de donnes. Pour cela, ajoutez un


bouton ddition dans la barre de navigation. crivez dans le viewDidLoad :

// ajout du bouton modier


self.navigationItem.rightBarButtonItem = self.editButtonItem;
Puis, il faut supprimer lvnement lorsque lutilisateur appuie sur le bouton Supprimer :

- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// on rcupre lvnement supprimer
EKEvent *eventToRemove = [[[eventsToDisplay
objectAtIndex:indexPath.section] objectForKey:kArrayOfEvents]
objectAtIndex:indexPath.row];
// on lenlve de la base de donnes
[eventStore removeEvent:eventToRemove span:EKSpanThisEvent error:nil]; 
// -> storeChanged sera automatiquement appel
// on lenlve du tableau (pour viter que a ne plante la ligne suivante)
[[[eventsToDisplay objectAtIndex:indexPath.section] objectForKey:kArrayOfEvents]
removeObjectAtIndex:indexPath.row];
// on supprime la vue avec animation
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}

Info
Lorsque la ligne  est excute, le contenu de la base de donnes change, appelant automatiquement la mthode storeChanged: qui raffichera les nouvelles donnes.
Faites attention au paramtre EKSpanThisEvent de cette mme ligne qui signifie : Ne supprimer
que cet vnement. Il existe galement EKSpanFutureEvents qui supprimera cet lment et tous les
lments futurs lis (sil y a rcurrence de lvnement). Le mieux est de demander lutilisateur ce
quil souhaite faire !

01_BlocN_iPhone.indd 276

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

MODIFIER

UN VNEMENT

Nous allons solliciter un contrleur de la classe EKEventViewController pour modifier ou visualiser


un vnement. Ldition ne sera autorise quen mode dition. Pour cela, il faut activer la possibilit
de slectionner une ligne en mode dition en rajoutant dans le viewDidLoad :

// autoriser la selection en mode dition


self.tableView.allowsSelectionDuringEditing = YES;
Ensuite, implmentez cette mthode :

- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
EKEventViewController *eventViewController = [[EKEventViewController alloc] init];
// on passe lvnement visualiser ou diter
eventViewController.event = [[[eventsToDisplay
objectAtIndex:indexPath.section] objectForKey:kArrayOfEvents]
objectAtIndex:indexPath.row];
// on nautorise ldition seulement lorsque la table view est en mode ddition
eventViewController.allowsEditing = self.tableView.editing;
// on prsente le contrleur
[self.navigationController pushViewController:eventViewController animated:YES];
[eventViewController release];
}
Voil, vous savez accder aux calendriers prsents sur votre appareil.
Pour aller plus loin, sachez que vous pouvez crer des vnements plus complexes avec des rgles de
rcurrences pousses. Lorsque cest possible, utilisez les contrleurs que nous avons vus avant de
crer votre propre interface pour crer/modifier des vnements.

Info
Noubliez pas le dealloc !

- (void)dealloc {
[eventsToDisplay release];
[eventStore release];
[[NSNoticationCenter defaultCenter] removeObserver:self];
[super dealloc];
}

29

Accder votre calendrier

01_BlocN_iPhone.indd 277

277

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

30

Intgrer de la publicit : iAd

278

30 Intgrer de la publicit : iAd


Lorsque vous souhaitez distribuer votre application, vous avez plusieurs choix parmi un
prix fixe lachat, lajout de publicits incorpores ou le don sous forme dIn App Purchase.
Apple a introduit avec iOS4 un service de publicit nomm iAd.
Une application gratuite est en moyenne tlcharge dix fois plus quune payante. Ainsi, si vous
souhaitez distribuer votre application en plus gros volume, ce choix peut tre judicieux. Cependant,
vous pouvez esprer obtenir une rmunration pour votre travail. Cela peut se faire sous forme de
dons, grce lIn App Purchase, ou en intgrant de la publicit votre application qui vous rmunrera
en nombre de clics et de vues. Le choix du type de distribution est primordial dans la gestion de votre
projet et nest pas universel. Une application payante 1,99 ou plus par exemple, reste perue
comme un gage de qualit, alors quune application gratuite peut tre considre comme une version
bride ou une application ne prsentant pas de grand intrt. Rassurez-vous, cette tendance change,
mais le public vis ne reste pas le mme. Il suffit de comparer les commentaires sur une application
gratuite avec une payante pour sen rendre compte. Gratuit, les gens acquirent votre application de
manire impulsive sans forcment de besoin et vous pouvez ne pas les toucher, donc rcolter un
mauvais commentaire.
La publicit intgre se prsente sous forme de bannires dune cinquantaine de pixels de haut. L
encore, quand vous concevez le design de votre application, cette contrainte est prvoir ! Jusqu
maintenant, les publicits intgres provenaient majoritairement de Admob mais Apple vient dintroduire iAd, un service de publicit directement intgr dans le SDK visant mieux rmunrer les
dveloppeurs.
lheure o ces lignes sont crites, je ne connais pas de manire sre le taux de rmunration (rmunration par vue et par clic) mais Apple conservera 40 % des gains et vous en reversera 60 %. A priori,
ce sera 10 $ pour 1 000 impressions (CPM) et 2 $ par clic, ce taux tant un des plus hauts du march.
La force diAd rside dans le HTML5 qui propose une interactivit avec lutilisateur trs pousse. Les
publicits deviendront des mini-applications, et seront attractives.

Info
Peut-tre vous posez-vous la question suivante : Et si je vendais mon application, tout en intgrant
de la publicit pour doubler mes revenus ? Cest dconseiller : un logiciel payant doit tre agrable
manipuler, et lajout de publicits peut vite agacer. Cette pratique, rare, est trs mal perue par
lacheteur qui ne manquera pas de vous le faire remarquer !
Rflchissez bien la position de votre bannire publicitaire, pour que lutilisateur ait plus de
chances de cliquer dessus malencontreusement, sans le forcer non plus. Cette astuce peut vous
choquer, mais elle se rvle plutt efficace et des tudes ont montr que la rmunration tait plus
importante lorsque la bannire publicitaire tait place en bas de lcran plutt quen haut.

01_BlocN_iPhone.indd 278

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

INTGRER

LE PLUS SIMPLEMENT DU MONDE I A D

Commencez par crer un nouveau projet Window-based Application que vous nommerez iAd.
Ajoutez un nouveau fichier de type UIViewController que vous nommerez RootViewController
(laissez coch Use XIB for user interface). Initialisez-le dans lapplication delegate, puis ajoutez sa vue
window. Ajoutez dans votre projet le framework iAd. Noubliez pas de spcifier la taille de la vue du
RootViewController pour quelle prenne en compte la prsence ou non de la barre de statut :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
rootViewController = [[RootViewController alloc]
initWithNibName:@RootViewController bundle:nil];
rootViewController.view.frame = [[UIScreen mainScreen] applicationFrame];
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
Dans RootViewController.h, importez ce framework et dclarez un objet de la classe ADBannerView :

#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
@interface RootViewController : UIViewController {
aDBannerView *adView;
}
@end
Puis, dans RootViewController.m, nous allons ajouter cette bannire en haut de lcran :

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {


if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.currentContentSizeIdentier = ADBannerContentSizeIdentier320x50;
// on choisit dafcher la bannire selon la taille prdnie en mode portrait
[self.view addSubview:adView];
}
return self;
}

Info
Encore une fois, noubliez pas vos dealloc ! Faites un release de rootViewController dans
iAdAppDelegate.m et de adView dans RootViewController.m.

30

Intgrer de la publicit : iAd

01_BlocN_iPhone.indd 279

279

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

30

280

Intgrer de la publicit : iAd

Lancez votre application.Vous devriez obtenir quelque chose danalogue la Figure 30.1. Lorsque vous
cliquez sur la bannire, une vue remplit lcran prsentant la publicit comme la Figure 30.2.

Figure 30.1 : La bannire de publicit.

Info
Les dimensions de votre bannire sont donc de 320 50 pixels
en mode portrait et de 480 32 pixels en mode paysage.
Lutilisation de notre bannire nest pas optimale, il faut implmenter le protocole delegate : ADBannerViewDelegate.

Figure 30.2 : La vue de la publicit.

RPONDRE

AU PROTOCOLE

ADBANNERVIEWDELEGATE

Le protocole vous propose ces mthodes :

bannerViewDidLoadAd: Appele lorsque la publicit a bien t charge.


bannerViewActionShouldBegin:willLeaveApplication: Appele lorsque lutilisateur clique sur la
bannire de publicit. En ralisant cette action, la publicit peut amener faire quitter lapplication, vous
en serez avertis. Cependant, vous navez rien faire pour sauvegarder ltat actuel de votre application, cela sera ralis automatiquement.Typiquement, il faut annuler ici toutes les mthodes qui pourraient demander lutilisateur une action (ne pas afficher dalert view, mettre votre jeu en pause...).
Cette mthode retourne un boolen. Si vous retournez YES, une page de publicit couvrira votre
cran. linverse, retourner NO empchera laffichage dune nouvelle vue.

Info
Noubliez pas de considrer que le multitche nest pas disponible sur tous les appareils !

bannerViewActionDidFinish: Lorsque lutilisateur revient de la page de publicit, cette mthode


sera appele.

bannerView:didFailToReceiveAdWithError: Votre application doit rpondre aux erreurs, il est


donc primordial dimplmenter cette mthode. Pour tester, il suffit dactiver le mode avion sur
votre appareil.

01_BlocN_iPhone.indd 280

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Nous allons voir comment afficher la publicit si elle est dj charge. Cela vite de faire apparatre
un bandeau vide disgracieux. Pour cela, modifiez les lignes en gras dans RootViewController.h :

@interface RootViewController : UIViewController <ADBannerViewDelegate> {


aDBannerView *adView;
BOOL adBannerIsVisible;
}
@end
Puis dans le .m :

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {


if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.delegate = self;
adView.currentContentSizeIdentier = ADBannerContentSizeIdentier320x50;
adView.alpha = 0.0; // met la publicit en transparent
[self.view addSubview:adView];
}
return self;
}
Nous allons prsenter notre publicit en ralisant un fondu. Vous pouvez galement choisir de la
dplacer pour la cacher hors de lcran par exemple.
Ajoutez ces lignes pour afficher la publicit lorsquelle est charge :

- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if(!adBannerIsVisible) // si la pub nest pas dj afche
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
adView.alpha = 1.0;
[UIView commitAnimations];
adBannerIsVisible = YES;
}
}
Ensuite, on choisit de lenlever si elle na pas pu tre charge :

- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error


{
// Appele lorsque la pub na pas russi tre tlcharge
if(adBannerIsVisible) // si la pub est visible
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];

30

Intgrer de la publicit : iAd

01_BlocN_iPhone.indd 281

281

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

30

282

Intgrer de la publicit : iAd

adView.alpha = 0.0;
[UIView commitAnimations];
adBannerIsVisible = NO;
}
}

AFFICHER

LA PUBLICIT SELON LORIENTATION DE LCRAN

Jusquici, nous avons affich la bannire de publicit en mode portrait seulement. Pour lafficher galement en mode paysage, il faut tout dabord spcifier la taille de la bannire pour les diffrents modes :

adView.currentContentSizeIdentier = ADBannerContentSizeIdentier320x50;
adView.requiredContentSizeIdentiers = [NSSet setWithObjects:
ADBannerContentSizeIdentier320x50, ADBannerContentSizeIdentier480x32, nil];
// ajouter
adView.alpha = 0.0; // met la publicit en transparent
Ensuite, autoriser la rotation de la vue (ici, on autorise toutes les positions) :

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)
interfaceOrientation {
return YES;
}
Puis changer la taille de la bannire selon lorientation qui va tre prise en compte :

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)
toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation))
adView.currentContentSizeIdentier = ADBannerContentSizeIdentier480x32;
else
adView.currentContentSizeIdentier = ADBannerContentSizeIdentier320x50;
}

ACTIVER IAD

POUR VOTRE APPLICATION

Tout dabord, vous devez accepter le contrat pour bnficier de iAd dans vos applications comme
la Figure 30.3. Ensuite, chaque soumission dapplication, il vous sera demand si vous utilisez iAd
comme la Figure 30.4. Notez quune fois que vous avez accept iAd pour votre application, vous ne
pourrez plus lenlever sans soumettre un nouveau binaire.

CRER VOTRE PUBLICIT


Vous pouvez galement crer votre publicit et devenir annonceur. Pour cela, il faudra utiliser iAd JS
qui est une librairie JavaScript disponible dans votre espace dveloppeur.

01_BlocN_iPhone.indd 282

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 30.3 : Activez


le contrat iAd.

Figure 30.4 : Activez


ou non iAd lors
de la soumission
de votre application.

30

Intgrer de la publicit : iAd

01_BlocN_iPhone.indd 283

283

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

C HAPITRE 6
GESTION DES RESSOURCES

01_BlocN_iPhone.indd 285

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Nous avons vu comment manipuler les lments dinterface pour afficher des
donnes, ajouter des animations dans le but de rendre le tout plus vivant
et agrable. Cependant, un point cl manque votre application : grer les
ressources. Dans cette fiche, vous apprendrez traduire votre application,
grer la mmoire et sauvegarder des donnes.

01_BlocN_iPhone.indd 286

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

31

Traduire votre application


31 Traduire votre application

Le march franais ne reprsente quune petite portion du nombre de ventes dapplications. Pour obtenir gloire et succs, il est indispensable de prsenter lapplication dans la
langue maternelle des plus gros marchs : anglais, franais et allemand.
Dans cette fiche, nous allons apprendre quelques rudiments pour localiser votre application et
prsenter les donnes en sadaptant lutilisateur. Les donnes ncessaires qui doivent tre traduites
sont les suivantes :

texte affich lcran ;


les images contenant du texte ou des lments traduire ;
les sons jous lutilisateur sils sont vocaux ;
la prsentation des donnes (les dates, les chiffres...).

Votre application doit au minimum tre traduite en anglais pour esprer toucher des clients non
francophones. Deux oprations sont ncessaires : localiser du texte dans lapplication (lorsque le
changement de texte est dynamique) et changer les .xib en fonction de la langue (quand vous ne
modifiez pas ces lments dans votre application).
Vous pouvez vous-mme oprer cette traduction ou confier les textes des spcialistes.

Info
Le changement de langue de votre application dpendra des rglages de liPhone (en fonction de la
langue choisie).
Commencez par crer un nouveau projet que vous nommerez LocalizedApp. Ajoutez ensuite un
fichier de la classe UIViewController, avec un .xib, que vous nommerez RootViewController. Affichez
sa vue, comme notre habitude, par-dessus la fentre dans lapplication delegate.

LOCALISER

DU TEXTE

Par dfaut, vous pouvez changer la langue en modifiant le .plist comme la Figure 31.1 (proprit
Localization native development region). En fait, si votre application ne possde pas la traduction dune
langue, elle la prendra depuis la langue spcifie. Prfrez donc English !
1. Dans le fichier RootViewController.h, ajoutez ces deux lignes dans l@interface :

IBOutlet UILabel *label;


IBOutlet UIButton *button;
2. Ensuite, ouvrez le fichier RootViewController.xib et ajoutez ces deux lments dans la vue
comme la Figure 31.2.
3. Puis, reliez ces lments en cliquant tout dabord sur Files owner.
4. Dans longlet Connections de lInspecteur, reliez les lments dans la liste ceux de votre vue
(voir Figure 31.3).

31

Traduire votre application

01_BlocN_iPhone.indd 287

287

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

31

288

Traduire votre application

Figure 31.1 : Changez la


langue par dfaut.

Figure 31.2 : Ajout dun


bouton et dun label.

Figure 31.3 : Reliez


les lments.

01_BlocN_iPhone.indd 288

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Nous allons crer un fichier que nous rendrons localisable. En fait, ce fichier sera disponible pour
plusieurs langues et contiendra du texte pour notre application. Crez un nouveau fichier de type
strings, comme la Figure 31.4. Nommez-le Localizable.

Info
Le nom du fichier est trs important, car il est reconnu par le systme au mme titre que le fichier
dicne Icon.png ou limage de lancement : Default.png.

Figure 31.4 :
Cration dun fichier
de type strings.

Ensuite, nous allons localiser ce fichier. Pour cela, reprez-le dans Xcode, dans votre projet, puis
cliquez du bouton droit dessus et sur Get Info. Rendez-vous dans longlet General puis cliquez sur
Make File Localizable. Ajoutez ensuite une langue (fr) en cliquant sur Add Localization comme la
Figure 31.5.

Info
Si vous souhaitez ajouter litalien, ce sera it, lespagnol es...

31

Traduire votre application

01_BlocN_iPhone.indd 289

289

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

31

290

Traduire votre application

Figure 31.5 : Ajoutez une langue.

Vous devriez obtenir quelque chose de semblable la Figure 31.6.

Figure 31.6 : Un fichier avec plusieurs langues.

Revenez au RootViewController.m et ajoutez les lignes en gras :

- (void)viewDidLoad {
NSLog(@La langue actuelle est : %@, [[NSLocale currentLocale]
localeIdentier]);
label.text = NSLocalizedString(@TextLabel, @Un commentaire);
[button setTitle:NSLocalizedString(@TextButton, @)
forState:UIControlStateNormal];
[super viewDidLoad];
}
Pour localiser un contenu, il faut donc utiliser NSLocalizedString (cl, commentaire) tout simplement ! Oui mais l, il va traduire tout seul ? Pas du tout ! Il suffit dassocier les traductions aux cls.
Pour cela, se rendre dans Localizable.strings et cliquer sur English. Ajouter ensuite ces lignes :

TextLabel = Hi there;

01_BlocN_iPhone.indd 290

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

TextButton = Start;
On va maintenant traduire en franais : cliquez sur fr et ajoutez :

TextLabel = Bonjour !;
TextButton = Commencer;
Vous pouvez lancer votre application. Quittez puis changez la langue en vous rendant dans Rglages >
Gnral > International.Vous verrez que vos textes seront traduits selon les diffrentes langues !

Info
Pour traduire, nattendez pas le dernier moment pour placer vos NSLocalizedString. Rien ne
vous empche dajouter autant de fichiers de type strings que de langues disponibles dans votre
application la fin de votre dveloppement. Par contre, remplacer tous les lments de textes la
fin peut se rvler fastidieux...

Astuce
Si vous souhaitez par exemple afficher Compteur : 1 en franais et Counter : 1 en anglais, vous pouvez
trs bien faire :

label.text = [NSString stringWithFormat:@%@ : %d,


NSLocalizedString(@CompteurTrad, @), counterVariable];
Si vous souhaitez changer les lments de place (les constructions de phrases ne sont pas les
mmes suivant les langues) vous pouvez crire :
Franais :

TexteDifferent = Prnom : %1$@ ; Nom : %2$@;


Anglais :

TexteDifferent = Last name : %2$@ ; First name : %1$@;


Que vous utiliserez comme ceci :

label.text = [NSString stringWithFormat:NSLocalizedString(@TexteDifferent,


@Prenom nom), prenom, nom];

LOCALISER

UN

.XIB

Comme pour le fichier strings, vous pouvez localiser vos .xib en cliquant du bouton droit sur le fichier
.xib puis Get Info, et Make File Localizable. Cependant, faites ce travail la fin, car toute modification
du fichier .xib devra tre reporte dans les fichiers correspondants chaque langue. De plus, ce travail
peut se rvler fastidieux car il faut ouvrir Interface Builder pour chaque fichier, puis modifier un un
les lments pour les traduire. Si le terminal vous est familier, utilisez-le pour crer des fichiers .strings
automatiquement en fonction des lments dans votre .xib, ce que nous allons voir ici.
Ouvrez le fichier RootViewController.xib et ajoutez un bouton ainsi quun label comme la Figure 31.7.

31

Traduire votre application

01_BlocN_iPhone.indd 291

291

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

31

292

Traduire votre application

Figure 31.7 : Ajoutez un bouton


et un label.

Vrifiez ensuite que larchitecture dans le Finder est la mme que celle de la Figure 31.8. Si ce nest
pas le cas, modifiez les chemins dans les lignes suivantes commenant par ibtool, sinon, dplacez les
fichiers pour obtenir la mme architecture. Noubliez pas que vous devrez les rimporter sous Xcode
(sans les copier) !
Vous remarquez lexistence des dossiers .lproj qui contiennent les lments de ressources en fonction de la langue.
Ouvrez un terminal : Applications > Utilitaires > Terminal.Voici quelques commandes de base pour les
dbutants :

ls Permet de lister lensemble des fichiers/dossiers lendroit o vous tes.


cd UnRepertoire Permet de rentrer dans UnRepertoire.
cd .. Permet de revenir en arrire dans la hirarchie.

Figure 31.8 : Dans le Finder...

01_BlocN_iPhone.indd 292

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Info
Si votre rpertoire sappelle Un repertoire, (avec un espace dans le nom du rpertoire), faites cd Un\

repertoire pour vous y rendre !


Vous devriez tre plac dans le dossier Home de votre ordinateur. Faites ls pour voir les fichiers/
dossier cet emplacement, puis naviguez jusquau dossier de votre application avec cd.
Ensuite, excutez la commande suivante pour crer un fichier .strings contenant les lments de
texte du .xib slectionn.

ibtool --generate-strings-le NomFichier.strings NomFichier.xib


Dans notre cas (assurez-vous dtre dans le rpertoire qui contient le fichier RootViewController.xib),
faites :

ibtool --generate-strings-le RootViewController.strings RootViewController.xib


Retournez sous Xcode et rendez le fichier RootViewController.strings localisable, comme dans la
premire partie de cette fiche. Ajoutez la langue fr.
Modifiez les valeurs avec par exemple pour le fichier English :

/* Class = IBUILabel; text = Label; ObjectID = 4; */


4.text = Label;
/* Class = IBUILabel; text = First Name; ObjectID = 9; */
9.text = First Name;
/* Class = IBUIButton; normalTitle = Touch; ObjectID = 10; */
10.normalTitle = Touch;
et pour la partie franaise :

/* Class = IBUILabel; text = Label; ObjectID = 4; */


4.text = Label;
/* Class = IBUILabel; text = First Name; ObjectID = 9; */
9.text = Prnom;
/* Class = IBUIButton; normalTitle = Touch; ObjectID = 10; */
10.normalTitle = Touche !;

Info
Il se peut que les valeurs des cls ne soient pas les mmes. Pas daffolement, cest la valeur qui
compte (rappel : Cl = Valeur;)
Il ne vous aura pas chapp que les fichiers RootViewController.strings et Localizable.strings sont
tous deux dans les dossiers .lproj correspondants.

31

Traduire votre application

01_BlocN_iPhone.indd 293

293

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

31

294

Traduire votre application

Pour crer les .xib pour chaque langue partir du fichier RootViewController.strings, tapez dans le
terminal :

ibtool --strings-le Langue.lproj/NomFichier.strings


--write Langue.lproj/NomFichier.xib NomFichier.xib
Ce qui donne dans notre cas :

Gnration du RootViewController.xib pour langlais :


ibtool --strings-le English.lproj/RootViewController.strings
--write English.lproj/RootViewController.xib RootViewController.xib

Gnration du RootViewController.xib pour le franais :


ibtool --strings-le fr.lproj/RootViewController.strings
--write fr.lproj/RootViewController.xib RootViewController.xib

Info
Cette procdure prsente lnorme avantage de donner aux traducteurs un fichier texte (le .strings)
qui contient les lments traduire, sans dvoiler votre interface graphique. De plus, cette mthode
est bien plus facile utiliser que traduire un un les fichiers .xib.

LOCALISER DAUTRES

RESSOURCES

En plus du texte, votre application peut afficher des donnes susceptibles dtre diffrentes selon les
pays. Par exemple, si vous prsentez des units, vous pourrez vous servir de [NSLocale currentLocale] pour faire apparatre des mtres ou des miles, des degrs Celsius ou Fahrenheit.
Exemple :

if ([[[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem] boolValue]


== YES) {
// on utilise les mtres
}
else
// on utilise les miles
// retourne llment (. ou ,) utilis en tant que virgule
NSString *separation = [[NSLocale currentLocale]
objectForKey:NSLocaleDecimalSeparator];
Vous trouverez toutes les valeurs dans la documentation de la classe NSLocale.
Un autre exemple frappant est laffichage de la date. En France, on crit la date de cette manire
21 Juin 2010 qui correspond en anglais June 21 2010. Ainsi, pour formater la date en fonction du
pays, on utilise NSDateFormatter :

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];


// choix du type (ici jour mois anne)
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];

01_BlocN_iPhone.indd 294

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// choix de la langue
[dateFormatter setLocale:[NSLocale currentLocale]];
// cration dun objet NSString avec la date actuelle
NSString *date = [dateFormatter stringFromDate:[NSDate date]];
NSLog(@date : %@, date);
[dateFormatter release];
De mme, vous pouvez utiliser NSNumberFormatter pour formater correctement vos chiffres en fonction de la langue du tlphone.

31

Traduire votre application

01_BlocN_iPhone.indd 295

295

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

296

La mmoire
32 La mmoire

La gestion de la mmoire est primordiale dans le dveloppement de votre application


pour la rendre stable (pas de crashs intempestifs). Il faut savoir quune application qui
plante, notamment cause de la mmoire mal gre, est le principal motif de refus de
validation de la part dApple.
Tout ceci nest pas banal et peut faire peur un dveloppeur iPhone dbutant. Je vous conseille donc
de vous rfrer cette fiche aussi souvent que possible, ds que vous avez le moindre doute dans
lcriture de votre code. Jen profite pour vous donner un petit conseil : nattendez pas le dernier
moment (quelques heures avant la soumission) pour vous proccuper de cette gestion de la mmoire...
Essayez de mettre en place un mcanisme dcriture du code o vous voyez exactement ce quil va se
passer dans la RAM de lappareil. Beaucoup dapplications crashent cause dune mauvaise comprhension globale de la manipulation des variables.

Info
La gestion mmoire est devenue encore plus importante depuis le passage iOS4 et les applications
qui quittent trs rarement (suspendues les trois quarts du temps). En effet, lorsque vous
redmarrez votre application, vous repartez de zro. Par contre, les fuites saccumulent lorsque
vous suspendez/ractivez votre application.

LA

MMOIRE...

DE

QUOI PARLE-T- ON

Si vous tes programmeur en C, vous tes srement familier avec le malloc() pour allouer de la
mmoire et le free() pour la librer. Le systme dexploitation iPhone, contrairement Mac OS
nintgre pas de garbage collector (ou ramasse-miettes). Lorsque vous demandez dallouer de la
mmoire un objet, vous rservez un emplacement spcifique. Cet emplacement, dans le cas de
liPhone, ne sera pas libr automatiquement contrairement un systme avec un ramasse-miettes,
qui serait capable de dterminer si lemplacement mmoire rserv est utile ou non. Par contre,
quand votre application quittera, la mmoire sera libre et naffectera pas le systme.
La mmoire de votre appareil, que ce soit liPhone, liPod ou liPad est limite. Il existe donc deux cas
de figures :

Vous allouez de la mmoire des objets, mais vous ne la librez jamais. Il y a donc ce que lon
appelle des fuites (leaks) qui vont perturber le comportement de votre application jusqu la faire
planter.

Vous allouez de la mmoire beaucoup dobjets, sans librer les anciens dont vous navez plus
ncessairement lutilit.Votre application va donc saturer en mmoire et planter.
Dans ce dernier cas, vous devrez implmenter la mthode didReceiveMemoryWarning qui sera appele
lorsque le systme de liPhone naura plus de mmoire disponible pour votre application. Dans cette
mthode, il faudra donc librer tous les objets inutiles. Si vous nimplmentez pas cette mthode, vous
prenez le risque de voir votre application crasher un jour ou lautre.

01_BlocN_iPhone.indd 296

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

QUELQUES

NOTIONS

Commenons par expliquer les termes que vous rencontrerez tout au long de votre aventure. Chaque
objet a ce que lon appelle un compteur de rfrence ou en anglais retainCount. Cest un nombre,
positif ou nul, qui informe du nombre dallocations dun objet.

Retain. Augmente de 1 le compteur de rfrence dun objet.


Release. Diminue de 1 le compteur de rfrence dun objet.
Autorelease. Diminue de 1 le compteur de rfrence dun objet un certain moment dans le futur.
Alloc. Alloue de la mmoire pour un objet, et le retourne avec un compteur 1.
Copy. Fais une copie dun objet et le retourne avec un compteur 1.

La vie de votre objet compte trois tapes : la cration, la gestion, puis la destruction.
La cration dun objet passe par lappel la mthode alloc de NSObject puis par son initialisation,
par dfaut, lappel la mthode init (vous pouvez crer vos propres mthodes).
Par exemple,

MonObjet *objet = [[MonObjet alloc] init];


Aprs cette ligne, le compteur de rfrence de objet sera gal 1.

VOTRE MTHODE INIT


Vous pouvez crer votre propre mthode dinitialisation. Par exemple, si vous souhaitez initialiser un
objet dune classe Personne en lui passant un nom et un prnom :

Personne *unePersonne = [[Personne alloc] initWithLastName:@Dupont andFirstName:@Thierry];


Avec votre mthode dinitialisation :

- (void) initWithLastName:(NSString*)lastName andFirstName:(NSString*)rstName {


if(self = [super init]) 
{
self.lastName = lastName;
self.rstName = rstName;
}
return self;
}
La ligne  mrite que lon sy arrte un instant. Que fait-on ? Tout dabord, il y a lappel la mthode init de
super (super dsigne la classe dont on hrite, ici NSObject (Personne est une sous-classe de NSObject))
avec [super init]. On affecte donc self la valeur retourne (lobjet initialis par [super init]). Puis, on
teste la valeur de self. if(self = [super init]) revient crire if((self = [super init]) != nil) ce
qui revient tester si self ne pointe pas vers nil, donc existe.

32

La mmoire

01_BlocN_iPhone.indd 297

297

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

298

La mmoire

Ensuite, vient la phase de vie de votre objet. Cest l o il sera copi, modifi, sauvegard dans un
tableau. Prenons par exemple :

MonObjet *objet
// retain count
[objet retain];
// retain count
[objet release]
// retain count
[objet release]
// retain count

= [[MonObjet alloc] init];


1
2
1
0 -> lobjet est dtruit

la dernire ligne, avec release, on dtruit lobjet en mmoire car son compteur de rfrence
atteint 0. Ainsi, la mthode dealloc de la classe MonObjet est appele. La mthode dealloc doit donc
tre implmente. Typiquement, elle doit contenir entre autres :

la libration de tous les objets dclars dans le .h et utiliss dans votre fichier dimplmentation
(le .m) ;

larrt des observations (KVO) avec par exemple [[NSNoticationCenter defaultCenter]


removeObserver:self];

larrt des timers (NSTimer).


Ici, le dealloc serait :

- (void) dealloc {
[lastName release];
[rstName release];
[super dealloc];
}

ATTENTION
Dans le cas o Interface Builder gre un lment dinterface, avec le mot-cl IBOutlet, vous ne devez
pas release lobjet (sauf cas suivant) car ce nest pas vous qui le possdez. Cependant, lorsque
vous avez un objet gr par Interface Builder, et que vous utilisez les accesseurs (@property et @
synthesize), vous devez librer lobjet dans le dealloc.

UTILISER

LES ACCESSEURS

Comme dans tout langage objet, vous pouvez affecter des valeurs un objet dune instance de classe.
Par exemple avec la classe Personne, vous souhaitez changer le nom (name) dun objet president de
cette classe.
En utilisant retain, qui va incrmenter le compteur de rfrence :

- (void) setName:(NSString*)newName {
if(newName != name) // on ne met jour que si le nouveau est diffrent

01_BlocN_iPhone.indd 298

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

{
[name release];
name = [newName retain]; // on incrmente de 1 le retainCount de
newName, il vaut donc 2
}
}
Vous utilisez de cette manire cette mthode :

Personne *president = [[Personne alloc] init];


NSString *presidentName = [[NSString alloc] initWithString:@Dupont]; 
[president setName:presidentName]; 
[presidentName release]; 
// plus tard
[president release];

Info
Faites trs attention la petite subtilit suivante : if(newName != name) ne vient pas tester si
les deux objets NSString ont la mme valeur, mais si ce ne sont pas les mmes en terme de valeur
dadresse. Par exemple, si ladresse de newName est 0x45674 et celle de name est 0x25234, alors les
adresses sont diffrentes donc la condition est vraie. De plus, si la valeur de newName est @toto
et celle de name est galement @toto, la condition sera toujours vraie.
Pour tester si deux objets NSString ont des valeurs diffrentes, il faut crire if([newName

isEqualToString:name] == NO)
Il est ncessaire de bien comprendre ce quil se passe ici :
Ligne , on alloue et initialise un nouveau nom de prsident. presidentName a donc son compteur de
rfrence 1.
Ligne , on vient changer la proprit name de president. Dans setName:, on release lancien name,
puis on incrmente le compteur de rfrence de newName, soit presidentName, de 1 qui vaut donc 2
maintenant. Ensuite, on fait pointer name vers newName (on fait une rfrence).
Puis, ligne , on release presidentName, son compteur de rfrence est dcrment et vaut donc 1.
Ainsi, lobjet name de president pointe vers le NSString allou avec Dupont.
On peut galement utiliser copy :

- (void) setName:(NSString*)newName {
if(newName != name) // on ne met jour que si le nouveau est diffrent
{
[name release];
name = [newName copy]; // on copie newName. name pointe alors vers cette copie
}
}

32

La mmoire

01_BlocN_iPhone.indd 299

299

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

300

La mmoire

En principe, vous naurez pas crire les accesseurs explicitement, car lObjective-C introduit
@property et @synthesize pour le faire votre place et allger votre code. Ainsi, crire
@property(retain) NSString *name dans le .h et @synthesize name dans le .m revient exactement
au mme que dimplmenter la mthode setName: avec comme comportement retain (vous navez
donc pas crire cette mthode car elle est implicite).
De fait, vous pourrez changer le nom avec lune de ces deux lignes :

[president setName:newName];
// ou
president.name = newName;

Info
Vous trouverez trs souvent des accesseurs crit @property(nonatomic, copy) NSString *name
par exemple. Mais que signifie le mot-cl en gras ? Si vous utilisez nonatomic, cela sous-entend que
plusieurs threads pourront accder en mme temps lobjet name. Ainsi, il peut potentiellement y
avoir quelques soucis car la ressource ne sera pas bloque. Par contre, si vous recourez atomic,
qui est le comportement par dfaut si vous ne spcifiez rien, votre objet ne sera dtenu que par un
seul thread la fois.
Question performances, laccs un objet avec nonatomic est plus rapide quavec un objet atomic.

LAUTORELEASE
Peut-tre vous posez-vous la question en ce moment mme : comment faire pour ne pas avoir de
fuites dans ce cas-l ?

- (NSString*) returnUnStringEnMajuscule:(NSString*)aString {
NSString *stringARetourner;
stringARetourner = [[NSString alloc] initWithFormat:@%@ %@, aString,
[aString uppercaseString]];
// release nest jamais appel ! -> fuite
return stringARetourner;
}
Dans cet exemple de code, on alloue un objet, stringARetourner que lon retourne mais cet objet
nest pas libr de la mmoire ! En effet, parce que sa dclaration est locale, ds lors que lon sort de
la fonction, les objets et variables dclares localement ne sont plus accessibles. Cela ressemble une
mission impossible, car il nest pas possible de faire :

- (NSString*) returnUnStringEnMajuscule:(NSString*)aString {
NSString *stringARetourner;
stringARetourner = [[NSString alloc] initWithFormat:@%@ %@, aString,
[aString uppercaseString]];
return stringARetourner;
[stringARetourner release]; // trop tard
}

01_BlocN_iPhone.indd 300

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

En effet, le return quitte la mthode, la ligne o lon release stringARetourner ne sera jamais
appele ! Il nest pas possible non plus de faire :

- (NSString*) returnUnStringEnMajuscule:(NSString*)aString {
NSString *stringARetourner;
stringARetourner = [[NSString alloc] initWithFormat:@%@ %@, aString,
[aString uppercaseString]];
[stringARetourner release]; // trop tt !
return stringARetourner;
}
Ici, le release apparat bien trop tt ! Lobjet retourn nexistera plus en mmoire... Cela ne convient
pas. Dans ce cas de figure, on utilise lautorelease, on parle galement de bassin dautorelease (NSAutoreleasePool). Concrtement, au lieu dappeler release sur un objet, et donc librer la mmoire
immdiatement, lappel dautorelease place lobjet dans une sorte de boucle. Cette boucle contiendra
donc un nombre dobjets en attente dtre librs. un instant T, lorsque le processus courant aura
fini sa tche, tous les objets dans la boucle seront librs.
La solution est donc la suivante :

- (NSString*) returnUnStringEnMajuscule:(NSString*)aString {
NSString *stringARetourner;
stringARetourner = [[NSString alloc] initWithFormat:@%@ %@, aString,
[aString uppercaseString]];
[stringARetourner autorelease];
return stringARetourner;
}

ALLER

PLUS LOIN AVEC LAUTORELEASE

Par dfaut, les objets senregistrent dans le bassin dautorelease le plus proche, trs souvent celui cr
dans main.m :

#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
Cependant, vous pouvez (devez) crer vous-mme votre bassin dautorelease lorsque :

vous dtachez un nouveau thread : la cration dun bassin dautorelease pour le thread est obligatoire ;

vous allouez beaucoup dobjets en mme temps.

32

La mmoire

01_BlocN_iPhone.indd 301

301

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

302

La mmoire

Prenons ce morceau de code :

NSString *unString = @;
for (int i = 0; i < 1000000; i++)
{
unString = [unString stringByAppendingString:[NSString stringWithFormat:@%d, i]];
}
return unString;
(Le rsultat va donner 01234567891011121314.....999998999999).
Ici, on remarque que lon cre beaucoup dobjets dans une boucle qui a 1 000 000 ditrations. Dailleurs,
les mthodes stringByAppendingString: et stringWithFormat: sont toutes deux des mthodes de
classe retournant un objet (NSString) en autorelease. Ainsi, chaque itration, sont placs deux
nouveaux objets dans le bassin dautorelease.
Leffet sera le suivant : lorsque le bassin dautorelease librera les objets prsents, votre application
risquera de geler, car cette libration ne sera pas instantane.
Une solution est bien entendu dutiliser release directement :

NSString *unString = @;
for (int i = 0; i < 100000; i++)
{
NSString *unAutreString = [[NSString alloc] initWithFormat:@%@%d, unString, i];
[unString release]; // on release lancien unString
unString = unAutreString; // on pointe vers unAutreString
}
return unString;
Mais si vous souhaitez passer par des mthodes qui retournent des objets en autorelease, alors :

NSString *unString = @;
for (int i = 0; i < 1000000; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
NSString *unAutreString = [unString stringByAppendingString:[NSString
stringWithFormat:@%d, i]]; 
[unString release]; 
[unAutreString retain]; 
unString = unAutreString; 
[pool release]; 
}
return unString;
Que se passe-t-il exactement ici ?
Ligne , on cre un nouveau bassin dautorelease. Ainsi, ligne , les objets crs en autorelease
sont placs dans le bassin le plus prs, cest--dire celui de la ligne .

01_BlocN_iPhone.indd 302

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ligne , on release lancien unString car nous nen avons plus besoin.
Ligne , on applique retain unAutreString pour viter que le bassin dautorelease allou ligne 
ne le dtruise !
Enfin, ligne  on fait pointer unString vers unAutreString puis on release le bassin ligne  ce qui
dtruira tous les objets prsents dans ce bassin. Notez que lon pourrait tre un peu plus malin en ne
crant/dtruisant le bassin dautorelease que toutes les 1 000 itrations par exemple.

Info
Il faut faire trs attention avec lutilisation de lautorelease. En effet, lorsque vous dbutez, cela
semble magique car vous navez pas vous proccuper explicitement du cycle mmoire de votre
application. Grave erreur ! Je vous conseille trs fortement de ne recourir lautorelease que dans les
cas particuliers comme ceux prsents ci-dessus. Certes, cela ncessitera un temps dadaptation
et de comprhension, mais les gains seront immenses. Un des problmes majeurs de lautorelease
est que vous ne pouvez prdire le moment exact o les objets seront dtruits.

CONSEILS
Q UELQUES

ET ERREURS

CONSEILS SIMPLES

Utiliser les accesseurs. Lorsque vous crivez par exemple :


NSString *nouveauString = [[NString alloc] initWithString:@toto];
[unString release];
unString = [nouveauString copy];
[nouveauString release];
Il est prfrable dcrire :

NSString *nouveauString = [[NString alloc] initWithString:@toto];


self.unString = nouveauString;
[nouveauString release];
avec dans le .h :

@property (nonatomic, copy) NSString *unString;


et dans le .m :

@implementation VotreClasse
@synthesize unString;

Ne pas allouer/dtruire des objets inutilement. Si vous avez un objet dont une proprit
change, prfrez modifier la proprit de lobjet plutt quen recrer un nouveau. Il faut donc
raliser ce travail de rflexion en amont lors de la conception de vos classes.

Le simulateur est un pige. Je ne vous le rappellerai jamais assez : le simulateur consomme les
ressources de votre ordinateur, le comportement mmoire est donc totalement fauss, de mme
que lanalyse des fuites avec les instruments.

32

La mmoire

01_BlocN_iPhone.indd 303

303

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

304

La mmoire

DES

ERREURS CLASSIQUES

Ces erreurs sont issues de la ralit et reviennent frquemment sur le forum. Lisez attentivement
cette section pour en comprendre les subtilits !
Un tableau gre lui-mme la mmoire des objets quil contient (de mme quun dictionnaire (NSDictionary, ...)). Par exemple, pour remplir un tableau de 10 nombres, vous avez 2 solutions :

NSMutableArray *array = [[NSMutableArray alloc] init];


int i;
// ...
for (i = 0; i < 10; i++)
{
NSNumber *n = [NSNumber numberWithInt:i];
[array addObject:n];
}
ou :

NSMutableArray *array = [[NSMutableArray alloc] init];


int i;
// ...
for (i = 0; i < 10; i++)
{
NSNumber *n = [[NSNumber alloc] initWithInt: i];
[array addObject: n];
[n release];
}
Dans le dernier cas, il ny a aucune raison de ne pas faire le release sur n, car addObject: fais automatiquement un retain sur lobjet pass en paramtre.
Veillez ne pas release plus que ncessaire un objet. En effet :

rootViewController = [[RootViewController alloc] init];


[rootViewController release];
[rootViewController release];
Gnrera ce message derreur :

malloc: *** error for object 0x5e276f0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Ne pas allouer plus que ncessaire non plus ! Par exemple, ceci est incorrect :

NSString *unAutreString = [[NSString alloc] initWithString:@toto];


// ....
NSString *unString = [[NSString alloc] init]; 
unString = unAutreString;
Ligne , on alloue de la mmoire pour unString. Ensuite, ligne , on fait pointer unString vers
unAutreString. Lemplacement mmoire rserv pour unString initialement est donc perdu jamais,
il y a une fuite !

01_BlocN_iPhone.indd 304

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

LOUTIL DANALYSE

STATIQUE

Un outil danalyse statique est inclus depuis Xcode 3.2. Cet outil va parcourir votre code et appliquer
certaines mthodes pour dterminer les erreurs. Il est utile par exemple pour regarder les erreurs
de gestion mmoire. Crez un nouveau projet Window-based Application que vous nommerez
LeaksDetection, et crivez ceci dans lapplication delegate :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 80, 30)];
[window addSubview:label];
NSNumber *number = [[NSNumber alloc] initWithFloat:45.67f];
label.text = [NSString stringWithFormat:@%f, [number oatValue]];
[window makeKeyAndVisible];
return YES;
}
Ici, on cre donc deux fuites : lune concerne lobjet label qui nest pas libr et lautre number qui, lui
aussi, nest pas dtruit. Vous remarquerez cependant que votre projet compile sans warnings, et que
le label est bien affich lcran.
Lanons loutil danalyseur statique : faites Build > Build and Analyze. Loutil trouve deux erreurs
comme la Figure 32.1.

Figure 32.1 :
Les rsultats de
lanalyse statique.

Comme vous le constatez, les deux erreurs potentielles sont des fuites mmoire. Cliquez sur un des
rsultats pour voir le dtail comme la Figure 32.2. Vous apercevez que loutil vous explique votre
erreur ! lannotation numro 1, vous allouez un label, avec un retain count de 1, ce qui est normal.
Par contre, lannotation numro 2, loutil vous dit qu partir de ce point, il ne trouve plus aucune
rfrence lobjet label, qui nest toujours pas release. Il y a donc une fuite potentielle.

32

La mmoire

01_BlocN_iPhone.indd 305

305

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

306

La mmoire

Figure 32.2 :
Les explications
de loutil.
1

Info
Il faut tout de mme faire attention cet outil et rflchir avant de corriger. Je vous conseille mme
de prendre un snapshot de votre projet avant de commencer corriger ces erreurs en faisant File >
Make Snapshot. Cela aura pour effet de prendre votre projet en photo, et pouvoir revenir cette
version si vous rencontrez des erreurs par la suite. Noubliez jamais de tester le comportement de
votre application aprs toute correction de fuite.
Ici, il peut tre tentant de corriger la fuite comme ceci, en ajoutant la ligne en gras :

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 80, 30)];


[window addSubview:label];
[label release];
Cest une erreur ! En effet, deux lignes aprs, vous changez le texte du label ce qui fera planter
lapplication car label nexiste plus. Il faut donc placer le release aprs le changement de la proprit
text. Cet outil vous explique vos erreurs, mais ne vous les rsout pas !
Lanalyseur statique permet aussi de vous prvenir derreurs autres que de potentielles fuites mmoire.
Par exemple, en ajoutant ces lignes de code :

int i;
if (i == 3)
NSLog(@i = 3);
Loutil vous signalera : Variable i declared without initial value signifiant que la variable i na pas t
initialise.

01_BlocN_iPhone.indd 306

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Si vous souhaitez compiler chaque fois laide de lanalyseur statique, rendez-vous dans Project > Edit
Project Settings, puis dans longlet Configuration  comme la Figure 32.3.
Ensuite, nous allons dupliquer le mode de compilation debug. Pour cela :
1. Slectionnez-le, puis cliquez sur duplicate  et renommez-le en Analyzer.
2. Reportez-vous maintenant la capture cran de la Figure 32.4. Rendez-vous dans longlet Build
et choisissez la configuration Analyzer .
3. Cochez Run Static Analyzer pour cette configuration dans Build options .
4. Fermez la fentre et vous pouvez maintenant choisir de compiler en mode Analyzer comme
la Figure 32.5.

1
1

Figure 32.3 : La configuration


de votre projet.

Figure 32.4 : Changez les options de compilation.

Figure 32.5 : Choisissez le mode


de compilation Analyzer.

32

La mmoire

01_BlocN_iPhone.indd 307

307

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

32

308

La mmoire

UTILISER

UN INSTRUMENT...

LEAKS !

Pour vrifier les fuites dans votre code, vous pouvez galement lancer un instruments qui va analyser
le droulement de votre application.
Pour lancer loutil Leaks, cliquez sur Run > Run with Performance Tool > Leaks. Patientez quelques
instants avant que linstrument ne mesure les fuites.Vous devriez obtenir quelque chose danalogue
la Figure 32.6.
4
1

Figure 32.6 : La fentre


daccueil de Leaks.

En , en orange, est reprsent lvnement une fuite a t dtecte. Si vous cliquez sur la ligne reprsentant les fuites , vous obtiendrez une liste des fuites dtectes en . Ces informations ne permettent
pas pour linstant de trouver lorigine de la fuite. Nous allons activer la vue tendue  en cliquant sur
View > Extended Detail. Cette nouvelle vue permet de reprsenter la pile des instructions menant
la fuite. Dans cette pile sont reprsentes toutes les mthodes, mme celles qui ne vous appartiennent pas. Les vtres sont prcdes de licne avec le haut dun personnage. Nous remarquons
donc en  que la fuite provient de notre application delegate. Double-cliquez sur cette ligne pour
afficher la portion de code incrimine comme la Figure 32.7. Il ne reste plus qu corriger la fuite !

Figure 32.7 :
La ligne qui fuit.

01_BlocN_iPhone.indd 308

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

ACTIVER

LES ZOMBIES

Lorsque vous dboguez votre application, il se peut que vous libriez trop un objet. Dans ce cas,
les erreurs dans la console peuvent vite devenir incomprhensibles. Pour y remdier, il faut activer ce
que lon appelle les zombies. En ralisant ceci, les variables qui seront dtruites seront changes en
_NSZombie et la zone mmoire ne sera jamais marque comme libre. Ainsi, toute erreur de gestion
mmoire amenant normalement un plantage en rgle de votre application sera plus explicite dans la
console.
Pour activer ce mode, double-cliquez sur lexcutable comme la Figure 32.8. Ensuite, rendez-vous
dans longlet Arguments puis ajoutez une variable dans Variables to be set in the environment que
vous nommerez NSZombieEnabled avec comme valeur YES comme la Figure 32.9.

Figure 32.8 : Trouvez lexcutable.

Figure 32.9 : Activez les zombies.

Le rsultat est le suivant, avec un double release de number :


Avant :

LeaksDetection(5032,0xa00e14e0) malloc: *** error for object 0x5d0a950: double free


*** set a breakpoint in malloc_error_break to debug
Aprs :

LeaksDetection[5038:207] *** -[CFNumber release]: message sent to deallocated


instance 0x591edb0
Attention cependant, lutilisation de ce mode doit rester ponctuelle ! Ne laissez pas ce mode dans la
distribution de votre application, les utilisateurs ne voudraient pas voir arriver des zombies partout...

32

La mmoire

01_BlocN_iPhone.indd 309

309

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

310

Utilisation de SQLite
33 Utilisation de SQLite

SQLite est un systme de gestion de base de donnes libre et multiplate-forme, particulirement adapt aux mobiles. Il est, par exemple, aussi bien disponible sur iPhone que sur
Androd. Cest un avantage trs intressant en cas de portage raliser. De plus, ce systme
a t retenu par Apple dans limplmentation de Core Data, lautre solution de stockage
sur iPhone.
Dans cette fiche, nous allons nous servir dune base de donnes SQLite pour raliser un systme de
gestion de scores.

CRATION

DE LA BASE DE DONNES

Nous allons crer un fichier database.sql, qui va contenir la base de donnes de notre projet. Dans notre
exemple, nous crerons le fichier, puis nous y insrerons des donnes initiales, afin de voir quil est tout
fait possible de partir dun fichier contenant une base complte (extraite dun site Internet par exemple).
Cest laide du Terminal (Spotlight > Terminal) que nous allons crer cette base. Positionnez-vous ensuite
dans un dossier (pour moi cest le bureau), (cd Desktop) et lanons les commandes ncessaires :

#cration du chier
sqlite3 database.sql
-- cration de la Base de Donnes (BDD)
CREATE TABLE score ( id INTEGER PRIMARY KEY, pseudo VARCHAR(50), resultat INT);
INSERT INTO score (pseudo, resultat) VALUES (iPodishima, 5);
INSERT INTO score (pseudo, resultat) VALUES (Heyfeel, 4);
INSERT INTO score (pseudo, resultat) VALUES (Aurel, 3);
INSERT INTO score (pseudo, resultat) VALUES (Bidou, 2);
-- on quitte sqlite3
.quit
Il restera importer le fichier database.sql au projet que nous allons crer.

CRATION

DU PROJET IPHONE ET DE LINTERFACE GRAPHIQUE

Nous allons partir dune Window-based Application que vous pouvez nommer SQLite.
Vous pouvez ajouter tout de suite le fichier database.sql votre projet dans le dossier ressources ,
copiez-le, et nous allons commencer linterface graphique :

Ajoutez tout dabord une classe RootViewController hritant de UIViewController notre projet.
Ajoutez galement une classe ListeViewController hritant elle, de UITableViewController.
Nous allons maintenant implmenter une premire partie de linterface graphique, en affichant dans
notre ListeViewController une liste dlments. Je ne rentre pas dans les dtails, mais jutilise un

01_BlocN_iPhone.indd 310

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

RootViewController pour modifier plus facilement la hirarchie des vues en cas de ncessit. Cela ne
devrait plus vous poser de problmes arriv ici.

SQLiteAppDelegate.h
#import <UIKit/UIKit.h>
#import RootViewController.h
@interface SQLiteAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *rootViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
SQLiteAppDelegate.m
#import SQLiteAppDelegate.h
@implementation SQLiteAppDelegate
@synthesize window;
#pragma mark #pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
rootViewController = [[RootViewController alloc] init];
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[window release];
[rootViewController release];
[super dealloc];
}

RootViewController.h
#import <UIKit/UIKit.h>
@interface RootViewController : UIViewController {
UINavigationController *navController;
}
@end

33

Utilisation de SQLite

01_BlocN_iPhone.indd 311

311

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

312

Utilisation de SQLite

RootViewController.m
#import RootViewController.h
#import ListeViewController.h
@implementation RootViewController
- (void)loadView {
ListeViewController *listeViewController =
[[ListeViewController alloc] initWithStyle:UITableViewStylePlain];
navController = [[UINavigationController alloc]
initWithRootViewController:listeViewController];
[listeViewController release];
navController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
self.view = navController.view;
}
- (void)dealloc {
[navController release];
[super dealloc];
}
@end
Vrifions que tout fonctionne avant daller plus loin. Pour cela, dans ListeTableViewController,
procdez aux changements suivants :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return 5 ;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentier:CellIdentier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
cell.textLabel.text = @lment;
return cell;
}

01_BlocN_iPhone.indd 312

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Normalement, en compilant et en lanant votre application, vous devriez obtenir une UITableView
affichant cinq lments. Si ce nest pas le cas, rfrez-vous la Fiche 12 sur les UITableView.

IMPLMENTATION

DU

SQLMANAGER

Nous allons maintenant implmenter une classe Score hritant de NSObject qui va grer les scores
des joueurs :

Score.h
#import <Foundation/Foundation.h>
@interface Score : NSObject {
NSInteger primaryKey;
NSString *pseudo;
NSInteger resultat;
}
@property (assign, nonatomic, readonly) NSInteger primaryKey;
@property (nonatomic, retain) NSString *pseudo;
@property (assign, nonatomic, readonly) NSInteger resultat;
-(id)initWithKey:(NSInteger)k pseudo:(NSString *)n resultat:(NSInteger )s;
@end
Score.m
#import Score.h
@implementation Score
@synthesize pseudo, resultat, primaryKey;
-(id)initWithKey:(NSInteger)k pseudo:(NSString *)p resultat:(NSInteger )r{
primaryKey = k;
self.pseudo = p;
resultat = r;
return self;
}
@end
Passons limplmentation de la classe SQLManager hritant galement de NSObject. Cette classe sera
le cur de cette fiche et vous sera utile dans vos futurs dveloppements si vous dcidez dopter pour
SQLite. Dans un premier temps, nous allons simplement implmenter linitialisation de la base de
donnes et la lecture des scores. Nous verrons plus tard comment raliser une insertion et une
suppression dans notre base.

33

Utilisation de SQLite

01_BlocN_iPhone.indd 313

313

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

314

Utilisation de SQLite

Tout dabord, il faut ajouter la librairie libsqlite3.0.dylib votre projet afin dexploiter les commandes
SQLite dans votre projet.

SQLManager.h
#import <Foundation/Foundation.h>
#import <sqlite3.h>
// Import du framework sqlite
#import Score.h
// Import de la classe Score
@interface SQLManager : NSObject {
// Variables de la Base de Donnes
NSString *databaseName;
NSString *databasePath;
// Tableau de Scores
NSMutableArray *scores;
}
@property (nonatomic, retain) NSMutableArray *scores;
-(id) initDatabase;
-(void) readScoresFromDatabase;
@end
SQLManager.m
#import SQLManager.h
@implementation SQLManager
@synthesize scores;
- (id) initDatabase {
if(self = [super init]) { 
//On dnit le nom de la base de donnes
databaseName = @database.sql;
// On rcupre le chemin
NSArray *documentPaths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir
stringByAppendingPathComponent:databaseName];
// On vrie si la BDD a dj t sauvegarde dans liPhone de
lutilisateur

BOOL success;
// Cre un objet FileManagerCreate qui va servir vrifer le statut
// de la base de donnes et de la copier si ncessaire

01_BlocN_iPhone.indd 314

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSFileManager *leManager = [NSFileManager defaultManager];


// Vrie si la BDD a dj t cre dans les chiers system de lutilisateur
success = [leManager leExistsAtPath:databasePath];
// Si la BDD existe dj on ne fait pas la suite
if(!success){
// Si ce nest pas le cas alors on copie la BDD de lapplication
vers les chiers systme de lutilisateur
// On rcupre le chemin vers la BDD dans lapplication
NSString *databasePathFromApp = [[[NSBundle mainBundle]
resourcePath] stringByAppendingPathComponent:databaseName];
// On copie la BDD de lapplication vers le chier systeme de
lapplication

[leManager copyItemAtPath:databasePathFromApp toPath:databasePath


error:nil];

[leManager release];
}
[databasePath retain];
}
return self;
}
- (void) dealloc {
[databasePath release];
[databaseName release];
[scores release];
[super dealloc];
}
@end
 La mthode initDatabase permet de rcuprer la base de donnes partir du fichier que nous
avons cr prcdemment. Cet appel initialise galement databasePath qui est le chemin de la base
qui va nous permettre dutiliser la base lorsque lon en aura besoin.

-(void) readScoresFromDatabase {
// Dclaration de lobjet database
sqlite3 *database;
// Initialisation du tableau de score 
if(scores == nil){
scores = [[NSMutableArray alloc] init];
}

33

Utilisation de SQLite

01_BlocN_iPhone.indd 315

315

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

316

Utilisation de SQLite

else {
[scores removeAllObjects];
}
// On ouvre la BDD partir des chiers systme
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Prparation de la requte SQL qui va permettre de rcuprer les
objets score de la BDD
//en triant les scores dans lordre dcroissant
const char *sqlStatement = select * from score ORDER BY resultat DESC;
//cration dun objet permettant de connatre le status de lexcution de
la requte

sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement,
NULL) == SQLITE_OK) {
// On boucle tant que lon trouve des objets dans la BDD
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// On lit les donnes stockes dans le chier sql
// Dans la premire colonne on trouve du texte que lon place
dans un NSString
NSInteger key = sqlite3_column_int(compiledStatement, 0);
NSString *pseudo = [NSString stringWithUTF8String:(char
*)sqlite3_column_text(compiledStatement, 1)];
// Dans la deuxime colonne on rcupre le score dans un NSInteger
NSInteger resultat = sqlite3_column_int(compiledStatement, 2);
// On cre un objet Score avec les paramtres rcuprs dans la BDD
Score *score = [[Score alloc] initWithKey:key pseudo:pseudo
resultat:resultat]; 
// On ajoute le score au tableau
if(![scores containsObject:score])
[scores addObject:score];
[score release];
}
}
// On libre le compiledStamenent de la mmoire
sqlite3_nalize(compiledStatement);
}
//On ferme la bdd
sqlite3_close(database);
}

01_BlocN_iPhone.indd 316

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

La mthode readScoreFromDatabase permet la lecture de notre base de donnes SQLite et cre un


NSMutableArray contenant la liste des Scores. Ceci se passe en trois tapes :
 Initialisation du tableau scores ou rinitialisation sil existe dj.
 On ouvre la base de donnes partir de databasePath,  on cre la requte SQL et  on
parcourt les rsultats.
 Pour chaque rsultat, on cre un objet score que lon ajoute au tableau scores sil ny est pas dj.

Modifions maintenant ListeTableViewController pour afficher les scores prsents dans le tableau
scores (les modifications apporter sont en gras).
ListeTableViewController.h
#import <UIKit/UIKit.h>
#import SQLManager.h

@interface ListeViewController : UITableViewController {


SQLManager *manager;
}
@end
ListeTableViewController.m
- (id)initWithStyle:(UITableViewStyle)style {
if ((self = [super initWithStyle:style])) {
self.title = @Liste des Scores;
manager = [[SQLManager alloc] initDatabase];
[manager readScoresFromDatabase];
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [manager.scores count];
}
- (UITableViewCell *)tableView:(UITableView
*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentier:CellIdentier];

if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentier:CellIdentier] autorelease]; 
}

33

Utilisation de SQLite

01_BlocN_iPhone.indd 317

317

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

318

Utilisation de SQLite

Score *score = [manager.scores objectAtIndex:indexPath.row];


cell.textLabel.text = score.pseudo;
cell.detailTextLabel.text = [NSString stringWithFormat:@%d,score.resultat]; 
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)dealloc {
[manager release];
[super dealloc];
}
 Jai recours ici un modle particulier de cellule fournie dans le SDK qui permet lutilisation dun
detailTextLabel  afin de disposer le pseudo gauche et le score en align droite.

Si tout va bien ce niveau, vous devriez voir safficher la liste initiale des scores de votre base de
donnes. (Figure 33.1)

Figure 33.1 : La liste des scores.

A JOUT DUN

SCORE DANS LA DATABASE.SQL

Nous allons modifier le manager et notre interface graphique afin dajouter un score notre base de
donnes. Pour cela, il faut implmenter une mthode permettant de faire une insertion dans la base
SQLite

SQLManager.m
- (void)insertIntoDatabase:(Score*)newScore {
// Dclaration de lobjet database
sqlite3 *database;
// On ouvre la BDD partir des chiers systme
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {

01_BlocN_iPhone.indd 318

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// Prparation de la requte SQL qui va permettre dajouter un score la BDD


NSString *sqlStat = [NSString stringWithFormat:@INSERT INTO score (pseudo,
resultat) VALUES (%@, %d);,newScore.pseudo, newScore.resultat];
//conversion en char *
const char *sqlStatement = [sqlStat UTF8String];
//On utilise sqlite3_exec qui permet trs simplement dexcuter une
requte sur la BDD
sqlite3_exec(database, sqlStatement,NULL,NULL,NULL); 
[self readScoresFromDatabase]; 
}
sqlite3_close(database);
}
 On utilise sqlite_exec pour excuter directement une requte dans notre base.
 Aprs avoir fait linsertion, on appelle readScoreFromDatabase afin de remettre jour le
tableau score.

Noubliez pas dcrire dans le . h :

- (void)insertIntoDatabase:(Score*)newScore;
On pourrait galement ajouter directement newScore au tableau scores, mais il faudrait ensuite trier
le tableau avant de rafrachir la UITableView.
Passons aux modifications de linterface graphique en crant tout dabord une nouvelle classe AjoutViewController hritant de UIViewController.

AjoutViewController.h
#import <UIKit/UIKit.h>
#import SQLManager.h
@interface AjoutViewController : UIViewController {
UITextField *pseudo;
UITextField *score;
SQLManager *manager;
}
@property(nonatomic, retain) SQLManager *manager;
@end
AjoutViewController.m
#import AjoutViewController.h
#import Score.h
@implementation AjoutViewController
@synthesize manager;
- (void)loadView {
self.view = [[[UIView alloc] init] autorelease]; 

33

Utilisation de SQLite

01_BlocN_iPhone.indd 319

319

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

320

Utilisation de SQLite

//Cration dun UITexField pour entrer le pseudo


pseudo = [[UITextField alloc] initWithFrame:CGRectMake(100, 60, 120, 20)];
pseudo.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0];
pseudo.textAlignment = UITextAlignmentCenter;
//on fait apparatre tout de suite le clavier
[pseudo becomeFirstResponder];
pseudo.placeholder = @Pseudo;
[self.view addSubview:pseudo];
[pseudo release];
//Cration dun UITexeld pour le score
score = [[UITextField alloc] initWithFrame:CGRectMake(100, 100, 120, 20)];
score.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1.0];
score.textAlignment = UITextAlignmentCenter;
//On utilise un clavier numrique
score.keyboardType = UIKeyboardTypeNumberPad;
score.placeholder = @Rsultat;
[self.view addSubview:score];
[score release];
//Cration dun bouton pour raliser lajout
UIButton *bouton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[bouton setTitle:@ajouter forState:UIControlStateNormal];
[bouton addTarget:self action:@selector(valider)
forControlEvents:UIControlEventTouchUpInside];
bouton.frame = CGRectMake(100, 140, 120, 30);
[self.view addSubview:bouton];
}
//mthode de validation
-(void)valider{
//Cration dun object Score partir des informations entres
Score *newScore = [[Score alloc] initWithKey:-1 pseudo:pseudo.text
resultat:[score.text intValue]];
//insertion du score
[manager insertIntoDatabase:newScore];
[newScore release];
[[self.navigationController.viewControllers objectAtIndex:0] viewWillAppear:YES]; 
[self.navigationController popViewControllerAnimated:YES];
}
- (void)dealloc {
[manager release];
[super dealloc];
}
@end

01_BlocN_iPhone.indd 320

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

 Il faut bien penser initialiser une vue notre contrleur car ici, il ny a pas de XIB associ.
 Je prviens linstance de ListeViewController que sa vue va bientt apparatre, afin que la mthode
viewWillAppear soit appele.

Il faut maintenant modifier ListeTableViewController de la manire suivante :

#import ListeViewController.h
#import AjoutViewController.h
- (id)initWithStyle:(UITableViewStyle)style {
if ((self = [super initWithStyle:style])) {
self.title = @Liste des Scores;
//cration dun bouton pour lajout
UIBarButtonItem *add = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self
action:@selector(ajoutScore)];
self.navigationItem.rightBarButtonItem = add;
[add release];
manager = [[SQLManager alloc] initDatabase];
[manager readScoresFromDatabase];
}
return self;
}
-(void)ajoutScore{
ajoutViewController *ajoutViewController = [[AjoutViewController alloc] init];
ajoutViewController.manager = manager;
[self.navigationController pushViewController:ajoutViewController animated:YES];
[ajoutViewController release];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//on recharge le Liste au retour aprs ajout
[self.tableView reloadData];
}

SUPPRESSION DUN

SCORE

Ajoutons une mthode de suppression notre manager. Cest le mme principe que pour lajout,
lexception de la requte SQL.

- (void)deleteFromDatabase:(Score*)oldScore {
// Dclaration de lobjet database
sqlite3 *database;

33

Utilisation de SQLite

01_BlocN_iPhone.indd 321

321

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

33

322

Utilisation de SQLite

// On ouvre la BDD partir des chiers systme


if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Prparation de la requte SQL qui va permettre de supprimer un score
la BDD
NSString *sqlStat = [NSString stringWithFormat:@DELETE FROM score WHERE
id = %d;,oldScore.primaryKey];
//conversion en char *
const char *sqlStatement = [sqlStat UTF8String];
//On utilise sqlite3_exec qui permet trs simplement dexcuter une
requte sur la BDD
sqlite3_exec(database, sqlStatement,NULL,NULL,NULL);
[self readScoresFromDatabase];
}
sqlite3_close(database);
}
Noubliez pas de dclarer dans le .h :

- (void)deleteFromDatabase:(Score*)oldScore;
Comme pour la mthode dajout, il faut penser appeler readScoreFromDatabase pour mettre jour
le tableau.
Passons linterface graphique, dans ListeViewController.m :

- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[manager deleteFromDatabase:[manager.scores objectAtIndex:indexPath.row]]; 
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:YES]; 
}
}
  : Il est important de raliser ces deux oprations dans cet ordre prcis, sinon votre application
quittera inopinment.

01_BlocN_iPhone.indd 322

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

Parser des fichiers XML et JSON


34 Parser des fichiers XML et JSON

Lorsque vous utiliserez des webservices, vous aurez trs certainement besoin de parser
des fichiers, quils soient XML ou JSON. Cette fiche vous apprend le faire.

UTILISER XML
Parsons un fichier XML contenant des pays. Crez un nouveau projet Window-based Application que
vous nommerez XML. crivez ces quelques lignes dans un fichier .xml (capitales.xml) et ajoutez-le au
projet (rappel : glisser-dposer le fichier directement dans Xcode).

<?xml version=1.0 encoding=UTF-8?>


<Monde>
<pays>
<nom>France</nom>
<capitale>Paris</capitale>
<habitants>60M</habitants>
</pays>
<pays>
<nom>Suisse</nom>
<capitale>Berne</capitale>
<habitants>7,5M</habitants>
</pays>
<pays>
<nom>Allemagne</nom>
<capitale>Berlin</capitale>
<habitants>82M</habitants>
</pays>
</Monde>
Il existe un parser XML intgr directement dans le framework Foundation : NSXMLParser. Nous
allons donc nous en servir. Pour cela, crez une nouvelle classe, subclass of NSObject que vous
nommerez XMLParser.
Implmentons cette classe en commenant par la mthode qui lancera le parsage :

- (void)parseXMLFileAtPath:(NSString *)path {
countries = [[NSMutableArray alloc] init]; 
NSURL *xmlURL = [NSURL leURLWithPath:path];
textParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[textParser setDelegate:self]; 
[textParser parse]; 
}

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 323

323

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

324

Parser des fichiers XML et JSON

Chaque pays prsent dans le fichier XML va tre ajout dans un tableau : countries, que lon initialise
ligne .
Ligne , la classe NSXMLParser propose un protocole delegate, et on choisit de limplmenter. Cest
obligatoire pour que le parser soit oprationnel. Enfin, on lance le parsage ligne .
Regardons maintenant le .h o lon dclare tous les objets ncessaires pour construire le tableau des pays.
#import <Foundation/Foundation.h>

#dene
#dene
#dene
#dene

kPays @pays
kCapitale @capitale
kNom @nom
kPopulation @habitants

@class XMLTableViewController;
@interface XMLParser : NSObject <NSXMLParserDelegate> {
NSXMLParser *textParser;
NSMutableArray *countries;
// un objet temporaire (ici un pays).
// il est ajout au tableau countries puis effac pour le nouveau pays
NSMutableDictionary *item;
// On va parser le document de haut en bas.
// On va donc collecter chaque sous-lements, les sauver dans item, puis
ajouter dans le tableau
NSString *currentElement;
NSMutableString *currentCountry;
NSMutableString *currentPopulation;
NSMutableString *currentCapital;
XMLTableViewController *tableViewController;
}
- (void)parseXMLFileAtPath:(NSString *)path;
@property (nonatomic, retain) NSMutableArray *countries;
@property (nonatomic, retain) XMLTableViewController *tableViewController;
@end
Noubliez pas ces lignes dans le .m :
#import XMLParser
#import XMLTableViewController

@implementation XMLParser
@synthesize countries;
@synthesize tableViewController;

01_BlocN_iPhone.indd 324

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ensuite, implmentons les mthodes du delegate. Commenons par celle appele lorsque le parser
rencontre un nouvel lment :

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName


namespaceURI:(NSString *)namespaceURI qualiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
if(currentElement)
{
[currentElement release];
currentElement = nil;
}
currentElement = [elementName copy]; 
if ([elementName isEqualToString:kPays]) { 
//On efface notre cache qui est item
if(item)
{
[item release];
item = nil;
}
if(currentCountry)
{
[currentCountry release];
currentCountry = nil;
}
if(currentCapital)
{
[currentCapital release];
currentCapital = nil;
}
if(currentPopulation)
{
[currentPopulation release];
currentPopulation = nil;
}
item = [[NSMutableDictionary alloc] init];
currentPopulation = [[NSMutableString alloc] init];
currentCapital = [[NSMutableString alloc] init];
currentCountry = [[NSMutableString alloc] init];
}
}
Dans cette mthode, on met jour llment courant rencontr ligne . Ensuite, si cet lment (la
balise rencontre) est gale Pays ligne , on sait que lon rencontre un nouveau pays. On remet
donc zro tous les objets qui servent construire item (un dictionnaire contenant les donnes du
pays).

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 325

325

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

326

Parser des fichiers XML et JSON

Aprs avoir dtect la balise Pays, il faut mettre en cache les valeurs des balises Nom, Capitale et
Population. Pour cela, implmentez la mthode suivante :

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{


// on sauve les lments du pays pour litem en cours
if ([currentElement isEqualToString:kCapitale])
[currentCapital appendString:string];
else if ([currentElement isEqualToString:kNom])
[currentCountry appendString:string];
else if ([currentElement isEqualToString:kPopulation])
[currentPopulation appendString:string];
}
Ensuite, dfinissons la mthode appele lorsque lon rencontre une balise de fermeture (exemple : </
balise>). Dans cette mthode, lorsque lon ferme pays, on sauve les valeurs des balises nom, capitale
et population pour le pays en question, puis on ajoute ce pays dans le tableau des pays.

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName


namespaceURI:(NSString *)namespaceURI qualiedName:(NSString *)qName{
if ([elementName isEqualToString:kPays]) {
// on sauve les valeurs dans item, qui est ensuite ajout dans le
tableau countries
[item setObject:currentCountry forKey:kNom];
[item setObject:currentCapital forKey:kCapitale];
[item setObject:currentPopulation forKey:kPopulation];
[countries addObject:item];
}
}
Enfin, lorsque le parser a fini de parcourir le document, on choisit de recharger la table view. En effet,
nous allons prsenter les donnes parses grce une table view, comme nous lavons vu la Fiche 13.
Pour cela, on appelle la mthode reloadData de la table view du table view controller tableViewController, proprit du parser. Cette mthode est appele sur le thread principal, car nous allons
par la suite parser dans un thread dtach pour loccasion.

- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(@Cest ni !);
NSLog(@countries a %d pays, [countries count]);
[tableViewController.tableView performSelectorOnMainThread:@selector(reloadData)
withObject:nil waitUntilDone:NO];
}

01_BlocN_iPhone.indd 326

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Et le dealloc ?

- (void) dealloc {
[countries release];
[textParser release];
[item release];
[currentCapital release];
[currentCountry release];
[currentPopulation release];
[super dealloc];
}
Crons maintenant le table view controller qui grera laffichage des donnes du fichier XML. Pour
cela, ajoutez un nouveau fichier comme la Figure 34.1 que vous nommerez XMLTableViewController.
Dans lapplication delegate, ajoutez ces lignes en gras :

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
xmlTableViewController = [[XMLTableViewController alloc]
initWithStyle:UITableViewStylePlain];
[window addSubview:xmlTableViewController.view];
[window makeKeyAndVisible];
return YES;
}

Figure 34.1 : Ajoutez un


table view controller.

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 327

327

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

328

Parser des fichiers XML et JSON

Ensuite, modifiez ces mthodes (lignes en gras) dans le table view controller frachement cr :

- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
NSString *xmlFilePath =[[NSBundle mainBundle] pathForResource:@capitales
ofType:@xml];
[self performSelectorInBackground:@selector(parseXMLFile:) withObject:xmlFilePath]; 
}
- (void)parseXMLFile:(NSString*)thePath {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
parser = [[XMLParser alloc] init];
parser.tableViewController = self; 
if ([parser.countries count] == 0)
{
[parser parseXMLFileAtPath:thePath];
}
[pool release];
}
Ligne , on choisit donc de parser le document dans un thread spar. Ainsi, le thread principal nest
pas ddi au parsage, et reste libre pour intercepter et ragir aux vnements utilisateurs, pour afficher des lments lcran. En dtachant un thread, on doit crer un bassin dautorelease, ce que lon
fait ligne . En effet, le bassin dautorelease cr dans le fichier main.m nest disponible que pour le
thread principal (voir Fiche 32). Pour permettre au parser dappeler le rafrachissement de laffichage
des donnes, on set la proprit tableViewController ligne .
Enfin, pour afficher les donnes :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Retourne le nombre de sections
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
// Retourne le nombre de lignes dans la section
return [parser.countries count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {

01_BlocN_iPhone.indd 328

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

static NSString *CellIdentier = @Cell;


UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentier:CellIdentier];

if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
NSDictionary *pays = [parser.countries objectAtIndex:[indexPath row]];
cell.textLabel.text = [NSString stringWithFormat:@%@ %@ %@,
[pays objectForKey:kNom], [pays objectForKey:kCapitale],
[pays objectForKey:kPopulation]];
return cell;
}
Noubliez pas le dealloc !

- (void)dealloc {
[parser release];
[super dealloc];
}
Pour information, voici le .h :

#import <UIKit/UIKit.h>
#import XMLParser.h
@interface XMLTableViewController :
UITableViewController {
XMLParser *parser;
}
@end
Compilez et lancez votre application, vous devriez obtenir
une liste comme la Figure 34.2.

Figure 34.2 : La liste des pays.

Info
Pour parser un fichier XML directement depuis une source Internet, il suffit de crer un objet NSURL
du fichier XML et le passer directement en paramtre de la mthode :

textParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 329

329

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

330

Parser des fichiers XML et JSON

UTILISER

LE

JSON

LiPhone et liPad sont des appareils mobiles et connects qui permettent un accs facile Internet.
Bien que lon puisse utiliser le navigateur intgr, grce une UIWebView, il est bien souvent plus
confortable pour lutilisateur de lancer une application spcifique liPhone, dite optimise, pour
accder aux informations qui lintressent, comme sa page Facebook ou son fil Twitter.
Cet accs est possible grce aux webservices qui sont en quelque sorte des sites web spcifiques,
sans mise en page, qui nous permettent dobtenir les informations ncessaires. Beaucoup de sites
proposent des API qui utilisent le JSON comme systme de fichier en plus du traditionnel XML.
Voici quelques exemples :

Twitter. http://apiwiki.twitter.com/Twitter-Search-API-Method%3A-trends-current
Facebook. http://wiki.developers.facebook.com/index.php/Comments.get
Mto. http://www.geonames.org/export/JSON-webservices.html
POURQUOI

LE

JSON ?

La question nest pas anodine. Le JSON a sduit les dveloppeurs par sa simplicit et sa rapidit. Mme
si ce nest pas une technologie mise en avant par Apple, qui utilise du XML pour les plist par exemple,
le JSON a lavantage dtre un format plus compact et donc plus rapide lors de son tlchargement
sur un webservice.
Comparaison sur un tableau de score par exemple :

XML :
<?xml version=1.0 encoding=UTF-8?>
<Resultats>
<Joueur>
<Nom>Jean Martin</Nom>
<Score>10000</Score>
</Joueur>
<Joueur>
<Nom>Pierre Dupond</Nom>
<Score>9000</Score>
</Joueur>
<Joueur>
<Nom>Alice Bateau</Nom>
<Score>8500</Score>
</Joueur>
</Resultats>
Longueur : 271 caractres.

01_BlocN_iPhone.indd 330

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

JSON :
{
resultats:{
joueurs:[
{
nom:Jean Martin,
score:10000
},
{
nom:Pierre Dupond,
score:9000
},
{
nom:Alice Bateau,
score:8500
}
]
}
}
Longueur : 194 caractres.
Que peut-on conclure de cette comparaison ? Le XML, de par sa taille, va tre environ 50 % plus lent
au tlchargement pour la mme information, ce qui est loin dtre ngligeable sur un objet mobile.

UTILISATION

SUR IPHONE

La mthode prsente nest pas LA mthode mais elle a le mrite de fonctionner parfaitement. Pour
corser le tout, nous allons faire une application compatible iPhone et iPad (Universelle).
Lancer Xcode et faire : File > New Project > Window-based Application et choisir Universal dans
Product. Appelons ce projet JSON_Tuto.

Info
Nous allons devoir utiliser un framework qui ne fait pas parti du SDK officiel afin de traiter les
fichiers en JSON.
Rappelez-vous, je vous ai dit quApple fonctionne avec XML pour le moment !
Il existe plusieurs frameworks pour traiter le JSON sur iPhone, notre prfrence va celui de disponible cette adresse : http://code.google.com/p/json-framework/. Il a lavantage davoir fait ses
preuves dans beaucoup dapplications.
Tlchargez la dernire version, vous devriez arriver sur la fentre reprsente la Figure 34.3.
Pour vous en servir, le plus simple est de glisser directement le dossier JSON dans votre projet
Xcode (et le copier).

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 331

331

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

332

Parser des fichiers XML et JSON

Figure 34.3 :
Le framework
JSON dans
le Finder.

Vous devriez obtenir la hirarchie prsente la Figure 34.4.

Figure 34.4 : Le framework JSON


dans votre application.

Nous allons maintenant intgrer un fichier en JSON dans notre projet afin de pouvoir lexploiter.
1. Sur JSON_Tuto faire c+Clic > Add > New File, pour ajouter un nouveau fichier o nous
crirons le JSON.
2. Choisir ensuite other dans la colonne de gauche puis Empty File.
3. Vous pouvez lappeler JSON.txt par exemple et ensuite y intgrer les informations que nous
avons utilises dans notre comparaison entre le JSON et le XML.
Vous devriez obtenir quelque chose de comparable la Figure 34.5.
Nous allons maintenant afficher ces donnes dans une UITableView. Pour ce faire : c+Clic > Add >
New File. Choisir ensuite Cocoa Touch Class > UIViewController et ne cocher que UITableViewController subclass comme la Figure 34.6.

Info
Il ne faut pas cocher la case de cration du fichier XIB, car cela nous obligerait en faire deux
diffrents : un pour liPad et un pour liPhone.

01_BlocN_iPhone.indd 332

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 34.5 : Votre


premier fichier JSON.

Figure 34.6 : Cration dun


Table View Controller.

Nommez ce fichier TableViewController.


Il reste ajouter cette vue aux deux Appdelegate (un pour liPad et lautre pour liPhone) comme vous
savez le faire maintenant :

AppDelegate_iPhone.h
#import <UIKit/UIKit.h>
@class TableViewController;
@interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {
UIWindow *window;
TableViewController *tableViewController;
}

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 333

333

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

Parser des fichiers XML et JSON

334

@property (nonatomic, retain) IBOutlet UIWindow *window;


@property (nonatomic, retain) TableViewController *tableViewController;
@end
AppDelegate_iPhone.m
#import AppDelegate_iPhone.h
#import TableViewController.h;
@implementation AppDelegate_iPhone
@synthesize window;
@synthesize tableViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
TableViewController *viewController = [[TableViewController alloc]
initWithStyle:UITableViewStylePlain];
self.tableViewController = viewController;
[viewController release];
[window addSubview:tableViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[window release];
[tableViewController release];
[super dealloc];
}
@end
Cest exactement la mme chose dans AppDelegate_iPad.h et AppDelegate_iPad.m :

AppDelegate_iPad.h
#import <UIKit/UIKit.h>
@class TableViewController;
@interface AppDelegate_iPad : NSObject <UIApplicationDelegate> {
UIWindow *window;
TableViewController *tableViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) TableViewController *tableViewController;
@end
AppDelegate_iPad.m
#import AppDelegate_iPad.h
#import TableViewController.h

01_BlocN_iPhone.indd 334

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

@implementation AppDelegate_iPad
@synthesize window;
@synthesize tableViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
TableViewController *viewController = [[TableViewController alloc]
initWithStyle:UITableViewStylePlain];
self.tableViewController = viewController;
[viewController release];
[window addSubview:tableViewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
[window release];
[tableViewController release];
[super dealloc];
}
@end
Avant daller plus loin, vous allez compiler votre projet pour le tester. Dans TableViewController.m
faites simplement :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Retourne le nombre de sections
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Retourne le nombre de lignes dans la section
return 1;
}
et :

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)
interfaceOrientation {
// Retourner YES pour les orientations dsires
return YES;
}
Cette dernire mthode permet votre application de fonctionner dans toutes les orientations.
Il est vivement recommand de limplmenter dans les applications iPad, daprs le guide dApple
concernant lInterface Homme Machine.
Vous devriez maintenant obtenir une liste de donnes vide et qui fonctionne dans toutes les orientations.

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 335

335

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

336

Parser des fichiers XML et JSON

REMPLIR

LA LISTE DEPUIS LE FICHIER

JSON

Afin de faciliter le travail et de bnficier de toute la puissance dun langage objet, nous allons crer
un objet Joueur. Pour cela, faites c+Clic > Add > New File. Crez un fichier Objective-C class
hritant de NSObject comme la Figure 34.7.

Figure 34.7 : Ajout dun nouveau


fichier hritant de NSObject.

Appelez cette classe Joueur tout simplement !


Passons maintenant limplmentation. Notre classe Joueur va respecter la structure du JSON et va
ainsi possder un nom et un score :

Joueur.h
#import <Foundation/Foundation.h>
@interface Joueur : NSObject {
NSString *nom;
NSInteger score;
}
@property (nonatomic, retain) NSString *nom;
@property NSInteger score;
@end
Joueur.m
#import Joueur.h
@implementation Joueur
@synthesize nom;
@synthesize score;
-(void)dealloc {
[nom release];
[super dealloc];
}
@end

01_BlocN_iPhone.indd 336

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

LE

TRAITEMENT DU

JSON

Cest la section la plus intressante. Un UITableViewController ncessite un tableau contenant les


donnes afficher (voir Fiche 12).
Dans notre cas, nous travaillerons avec une liste dobjets Joueur.

TableViewController.h
#import <UIKit/UIKit.h>
@interface TableViewController : UITableViewController {
NSMutableArray *liste;
}
@end

Info
Dans le format JSON, la chose importante est de distinguer les lments des tableaux.
Voici deux rgles qui permettent de sen sortir facilement :
Rgle 1 : le format normal est :

{ cl1 : lment1, cl2 : lment2 }


Rgle 2 : un lment peut tre un tableau dlments et un tableau scrit :

[{ sous_cl1 : sous_lment1} , {sous_cl2 : sous_lment2}]


Le rflexe est donc [ ] donne un tableau.
Dans notre cas, dans la cl resultat, on a un lment avec la cl Joueur qui contient un tableau de
joueurs !
Passons au code :

#import Joueur.h
#import JSON.h
@implementation TableViewController
- (void)viewDidLoad {
[super viewDidLoad];
liste = [[NSMutableArray alloc] init];
//rcupration du chemin vers le chier contenant le JSON
NSString *lePath = [[NSBundle mainBundle] pathForResource:@JSON ofType:@txt];
//cration dun string avec le contenu du JSON
NSString *myJSON = [[NSString alloc] initWithContentsOfFile:lePath
encoding:NSUTF8StringEncoding error:NULL];

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 337

337

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

34

Parser des fichiers XML et JSON

338

//Parsage du JSON laide du framework import


NSDictionary *json
= [myJSON JSONValue];
//rcupration des rsultats
NSDictionary *resultats
= [json objectForKey:@resultats];
//rcupration du tableau de Joueurs
NSArray *listeJoueur
= [resultats objectForKey:@joueurs];
//On parcourt la liste de joueurs
for (NSDictionary *dic in listeJoueur) {
//cration dun objet Joueur
Joueur *joueur = [[Joueur alloc] init];
//renseignement du nom
joueur.nom = [dic objectForKey:@nom];
//renseignement du score
joueur.score = [[dic objectForKey:@score] intValue];
//ajout la liste
[liste addObject:joueur];
//libration de la mmoire
[joueur release];
}
// ne pas oublier aprs lallocation effectue au dbut
[myJSON release];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [liste count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;

01_BlocN_iPhone.indd 338

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentier:CellIdentier];


if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
Joueur *joueur = [liste objectAtIndex:indexPath.row];
//on afche le nom et le score
cell.textLabel.text = [NSString stringWithFormat:@%@ - %d,joueur.nom, joueur.score];
return cell;
}
Sans oublier le dealloc bien sr !

34

Parser des fichiers XML et JSON

01_BlocN_iPhone.indd 339

339

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

35

340

Sauvegarder des donnes


35 Sauvegarder des donnes

Une application qui ne sauvegarde pas de donnes nest pas vraiment une application...
Cette fiche aborde la sauvegarde de donnes dans un fichier plist et lutilisation du protocole NSCoding.
Imaginez une application contenant des sliders, des switchs, ainsi quun champ de texte. Nous souhaitons sauvegarder les valeurs de chaque lment lorsque lutilisateur appuiera sur le bouton Home.
Pour cela, nous allons crer un objet de la classe NSDictionary qui contiendra les couples cls/valeurs
de chaque lment affich lcran. Cet objet sera dtenu par notre application delegate, et sera cr
partir du fichier plist de sauvegarde.
Mais tout dabord, quest-ce quun fichier plist ? Cest un type de fichier couramment utilis pour
sauvegarder des donnes dans lunivers Mac. Regardez par exemple sur votre ordinateur, dans
Bibliothques > Prfrences ! Plist signifie Property Lists. La structure dun plist est la suivante (vous
reconnaissez bien entendu le XML) :

<?xml version=1.0 encoding=UTF-8?>


<!DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version=1.0>
<dict>
<key>Nom</key>
<string>Albert Dupont</string>
<key>Telephones</key>
<array>
<string>0354231287</string>
<string>0964289302</string>
</array>
</dict>
</plist>
Comme on le constate, le plist contient ici un dictionnaire (balise dict) compos des couples cls/
valeurs.
Voici le code pour lire des donnes :

- (BOOL) readDataFromFile {
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory
stringByAppendingPathComponent:@preferences.plist];

NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];

01_BlocN_iPhone.indd 340

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSDictionary *dictionary = (NSDictionary *)[NSPropertyListSerialization


propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format
errorDescription:&errorDesc];

if (!dictionary) {
// Il y a eu une erreur, errorDesc est allou
NSLog(@erreur : %@,errorDesc);
[errorDesc release];
// retourner NO ventuellement selon limplmentation dsire
}
self.prefDictionary = [NSMutableDictionary
dictionaryWithDictionary:[dictionary objectForKey:@prefDictionary]];

return YES;
}
Et le code pour crire dans un fichier :

- (BOOL)writeDataToFile {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory
stringByAppendingPathComponent:@preferences.plist];
NSString *errorDesc;
NSMutableDictionary *prefDict;
// on remplace par les valeurs que lon a modies
prefDict = [[NSMutableDictionary alloc] initWithObjects:[NSArray
arrayWithObject:prefDictionary]
forKeys:[NSArray arrayWithObject:@prefDictionary]];
NSData *plistData = [NSPropertyListSerialization
dataFromPropertyList:prefDict format:NSPropertyListXMLFormat_v1_0
errorDescription:&errorDesc];

if (plistData) {
BOOL returned = [plistData writeToFile:plistPath atomically:YES];
return returned;
}
else {
[errorDesc release];
return NO;
}
return NO;
}

35

Sauvegarder des donnes

01_BlocN_iPhone.indd 341

341

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

35

342

Sauvegarder des donnes

Info
Nesprez par rcuprer/lire le fichier directement dans Xcode. En effet, il faut bien comprendre
que les fichiers de votre projet seront compils (les .h, .m, .cpp...) pour crer lexcutable de votre
application ou copis (les images, fichiers texte...) dans le dossier de lapplication. Par contre, ils ne
seront pas mis jour si votre application les modifie. Cela vous semble facile ? Tant mieux ! Je me
permets ce rappel car la question a dj t pose sur notre forum plusieurs fois...
Pour accder aux fichiers depuis votre iPhone, rendez-vous dans Xcode : Window > Organizer >
votre appareil > la liste des applications, puis la petite flche sur la droite. Ainsi, vous rapatrierez
les donnes de lapplication sur votre ordinateur pour les interprter. Pour rcuprer le dossier
du simulateur, il faut se placer dans le dossier racine de votre compte. Ensuite, Bibliothque >
Application Support > iPhone Simulator > la version de lOS sur lequel lapplication est compile >
lidentifiant de votre application > Documents.
Dans un nouveau projet de type View-based Application nomm PropertyList, placez un slider, un
switch et un text field ainsi quun bouton Valider dans la vue. Dans la mthode appele par le bouton,
nous allons sauver les tats courants de chaque lment :

- (void)freezeButton : (id) sender {


PropertyListAppDelegate *appDelegate =
(PropertyListAppDelegate*)[[UIApplication sharedApplication] delegate];
// On fait une rfrence vers le dictionary qui contient les donnes
NSMutableDictionary *prefDict =
(NSMutableDictionary*)appDelegate.prefDictionary;
NSNumber *sliderValue = [NSNumber numberWithFloat:aSlider.value];
[prefDict setObject:sliderValue forKey:kSlider];
[prefDict setObject:[NSNumber numberWithBool:aSwitch.on] forKey:kSwitch];
[prefDict setObject:aTextField.text forKey:kTextField];
}

Attention
Jattire votre attention sur cette ligne :

PropertyListAppDelegate *appDelegate = (PropertyListAppDelegate*)[[UIApplication


sharedApplication] delegate];
(Noubliez pas dimporter PropertyListAppDelegate.h).
Cette ligne fait pointer appDelegate vers lobjet de la classe PropertyListAppDelegate instanci par
votre application.

01_BlocN_iPhone.indd 342

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Noubliez pas ces quelques lignes pour dfinir les cls utilises :

#dene kSlider @slider


#dene kSwitch @switch
#dene kTextField @textField
En rcuprant le fichier preferences.plist comme prcis au dbut de la section, nous obtenons ceci
par exemple :

<?xml version=1.0 encoding=UTF-8?>


<!DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version=1.0>
<dict>
<key>prefDictionary</key>
<dict>
<key>slider</key>
<real>0.42418771982192993</real>
<key>switch</key>
<true/>
<key>textField</key>
<string>iPuP</string>
</dict>
</dict>
</plist>
On retrouve bien le dictionnaire avec la cl prefDictionary puis les valeurs de nos trois lments.

Astuce
Si vous avez besoin de sauver seulement quelques valeurs, comme un pseudo ou une taille de police,
vous pouvez utiliser plus simplement NSUserDefaults. Voici comment crire [[NSUserDefaults
standardUserDefaults] objectForKey:@aKey]; et lire [[NSUserDefaults standardUserDefaults]
setObject:anObjct forKey:@aKey];. Notez quil existe boolForKey:, integerForKey:, ...
Imaginons maintenant que vous souhaitez sauver des objets plus compliqus, trs facilement... Cela
fait rver, non ? Eh bien cest possible. Grce au protocole NSCoding, nous allons archiver les donnes
puis les enregistrer dans un fichier. En fait, vous allez trs vite vous rendre compte de la puissance de
NSCoding pour archiver des structures de donnes relies entre elles. Typiquement, les fichiers nib
utilisent ce protocole pour archiver votre hirarchie de vue, et la dpiler au lancement de lapplication.
Attention, le fichier nib ne sera pas modifi par votre application, mais seulement dsarchiv depuis la
valeur par dfaut lorsque vous avez compil le binaire.
Finalement, lobjet dsarchiv sera une copie conforme (proprits, dpendances...) de lobjet archiv.

35

Sauvegarder des donnes

01_BlocN_iPhone.indd 343

343

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

35

344

Sauvegarder des donnes

Un exemple concret serait une liste de matires. Crons donc une classe Matire implmentant le
protocole NSCoding :

#import <Foundation/Foundation.h>
#dene kNumero @number
#dene kMatiereName @Matiere
#dene kDateDebut @DateDebut
#dene kCouleur @CouleurCell
#dene kProfesseur @Professeur
#dene kSalle @Salle
@interface Matiere : NSObject <NSCoding> {
int number;
NSString *matiere;
NSString *dateDebut;
UIColor *couleur; // cet objet ne sert rien mais vous montre simplement que lon
peut archiver nimporte quel objet implmentant le protocole NSCoding
NSString *professeur;
NSString *salle;
}
- (id)initWithNumber:(int)myNumber color:(UIColor*)myColor date:(NSString*)myDate
teacher:(NSString*)myTeacher subject:(NSString*)mySubject room:(NSString*)myRoom;
@property int number;
@property (nonatomic, retain) NSString *subject;
@property (nonatomic, retain) NSString *date;
@property (nonatomic, retain) UIColor *color;
@property (nonatomic, retain) NSString *teacher;
@property (nonatomic, retain) NSString *room;
@end

Attention
Les objets que vous souhaitez archiver doivent implmenter le protocole NSCoding sinon cela ne
marchera pas. Par exemple, la classe UIImage nimplmente pas ce protocole. Par contre, la classe
NSData, si. Il faut donc archiver les donnes de votre image sous forme de NSData.
Le protocole NSCoding requiert limplmentation de deux mthodes : une servant archiver et lautre
dsarchiver.Voici ce que vous devez ajouter :

#pragma mark #pragma mark NSCoding


- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeInt:self.number forKey:kNumero];
[coder encodeObject:self.color forKey:kCouleur];

01_BlocN_iPhone.indd 344

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[coder
[coder
[coder
[coder

encodeObject:self.date forKey:kDateDebut];
encodeObject:self.teacher forKey:kProfesseur];
encodeObject:self.room forKey:kSalle];
encodeObject:self.subject forKey:kMatiereName];

}
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
self.number = [coder decodeIntForKey:kNumero];
self.color = [coder decodeObjectForKey:kCouleur];
self.date = [coder decodeObjectForKey:kDateDebut];
self.teacher = [coder decodeObjectForKey:kProfesseur];
self.subject = [coder decodeObjectForKey:kMatiereName];
self.room = [coder decodeObjectForKey:kSalle];
}
return self;
}
Noubliez pas ces deux mthodes (et les @synthesize) :

- (id)initWithNumber:(int)myNumber color:(UIColor*)myColor date:(NSString*)myDate


teacher:(NSString*)myTeacher subject:(NSString*)mySubject room:(NSString*)myRoom {
if (self = [super init]) {
self.number = myNumber;
self.color = myColor;
self.date = myDate;
self.teacher = myTeacher;
self.subject = mySubject;
self.room = myRoom;
}
return self;
}
-(void)dealloc{
[color release];
[date release];
[teacher release];
[room release];
[subject release];
[super dealloc];
}

35

Sauvegarder des donnes

01_BlocN_iPhone.indd 345

345

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

35

346

Sauvegarder des donnes

Enfin, pour lire depuis un fichier :

(BOOL) unarchiveSubject {
BOOL toReturn = NO;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path =
[documentsDirectory stringByAppendingPathComponent:@Matieres.myArchive];
if(path)
{
NSData *data;
NSKeyedUnarchiver *unarchiver;
data = [[NSData alloc] initWithContentsOfFile:path];
if(data)
{
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSMutableArray *arrayArchived =
[unarchiver decodeObjectForKey:@Matieres];

self.list = arrayArchived;
[unarchiver nishDecoding];
[unarchiver release];
toReturn = YES;
}
[data release];
return toReturn;
}
}
Et archiver :

(BOOL) archiveSubject {
NSMutableData *data;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *archivePath = [documentsDirectory
stringByAppendingPathComponent:@Matieres.myArchive];
NSKeyedArchiver *archiver;
BOOL result;
data = [NSMutableData data];
archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

01_BlocN_iPhone.indd 346

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

[archiver encodeObject:list forKey:@Matieres];


[archiver nishEncoding];
result = [data writeToFile:archivePath atomically:YES];
[archiver release];
return BOOL;
}

info
Vous pouvez crer ou utiliser nimporte quelle extension pour votre fichier de sauvegarde.

35

Sauvegarder des donnes

01_BlocN_iPhone.indd 347

347

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

348

Utiliser Core Data


36 Utiliser Core Data

Ou comment se simplifier la vie lorsque lon souhaite stocker des donnes.


Core Data est un framework cr par Apple qui stocke des donnes dans la mthode de conception
MVC (Model View Controller ou Modle Vue Contrleur). Vous me direz : Et pourquoi jutiliserais a ?
Je men sors tout seul ! Ce quoi je vous rpondrais que Core Data permet dans un premier temps
de rduire significativement la taille de votre code utilis pour stocker vos donnes (de lordre de
50 %) et dans un second temps, quil est entirement optimis et prouv.
Avant de commencer, il est donc ncessaire de comprendre les enjeux du MVC. MVC se compose de
trois entits distinctes :

Le Modle. Il contient lensemble des donnes qui seront manipules. Il propose donc un accs
ces donnes en lecture, et bien entendu des mthodes pour les modifier.

La Vue. Elle va prsenter ces donnes lcran et permet linteraction avec lutilisateur.
Le Contrleur. Il fait linterface entre la vue et le modle. Il reoit les vnements de la vue,
demande modifier les donnes en consquence, et avertit la vue que les donnes ont chang.
Tout ceci est rsum par un schma Figure 36.1.
Action de
lutilisateur

Met jour

Figure 36.1 : Le modle


Model View Controller.

Met jour

Notifie

Avec Core Data nous allons raliser une petite application en deux temps. Le but de cette application
sera de crer un profil demploy (Nom, Prnom, ge, Sexe).
Dans un premier temps, nous apprendrons utiliser Core Data en ajoutant des donnes grce une
vue cre pour loccasion. De plus, ces donnes seront tries, et nous pourrons les supprimer. Puis,
nous verrons comment grer ldition.

APPRENDRE

UTILISER

CORE DATA

Les prrequis sont la Fiche 12 sur les table view ainsi que les premires fiches pour apprhender
lunivers de dveloppement Cocoa Touch.
Crez un nouveau projet Window-based Application que vous nommerez CoreData. Noubliez pas de
cocher Use Core Data for storage. Vous remarquerez que, par rapport dhabitude, vous avez un
nouveau fichier : CoreData.xcdatamodel. Cest ce fichier qui va vous permettre de crer le graph des
donnes de votre modle. De plus, lapplication delegate contient des nouvelles mthodes, qui sont
utilises par Core Data pour grer les donnes.

01_BlocN_iPhone.indd 348

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Tout dabord crez votre graph de donnes. Pour cela, double-cliquez sur CoreData.xcdatamodel.
Vous devriez obtenir la fentre de la Figure 36.2.

Figure 36.2 : La fentre daccueil pour crer le graph de donnes.


Puis ajoutez une entit. Une entit correspond une classe. Faites :
1. Design > Data Model > Add Entity. Changez son nom pour Entreprise.
2. Ajoutez-lui un attribut, qui correspond une variable dinstance. Pour lentreprise, ce sera
simplement son nom.
3. Aprs avoir slectionn lentit Entreprise, faites Design > Data Model > Add Attributes.
4. Changez le nom pour nom et choisissez comme type : String.
Vous remarquerez que la liste des donnes disponibles est finie. Pensez-y !
Faites de mme en crant une entit Employe pour lequel vous ajouterez trois attributs de type
string : nom, prenom, sexe et un attribut de type integer 16 : age. Une entreprise a plusieurs employs,
mais un employ ici naura quune entreprise. En effet, avec Core Data, nous pouvons facilement
ajouter des relations entre les entits et cest ce qui fait sa puissance, entre autres. Pour ce faire :
1. Cliquez sur lentit Entreprise, et faites Design > Data Model > Add Relationship.
2. Choisissez comme nom employes puis dans Destinations lentit Employe.
3. Cochez To-Many Relationship, car une entreprise a plusieurs employs.
4. Cliquez ensuite sur lentit Employe et ajoutez une relation avec Entreprise nomme entreprise.
Un employ nappartenant qu une unique entreprise la fois, dcochez To-Many Relationship.

36

Utiliser Core Data

01_BlocN_iPhone.indd 349

349

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

350

Utiliser Core Data

Vous devriez obtenir quelque chose de semblable la Figure 36.3.

Figure 36.3 :
Les relations
entre Employe
et Entreprise.

Pour rendre les choses plus propres, cliquez sur lentit Employe puis sur entreprise et choisissez
employes dans Inverse. Cette fois-ci, la flche est devenue unique et bidirectionnelle comme la
Figure 36.4.

Figure 36.4 : Une


flche plus lisible.

01_BlocN_iPhone.indd 350

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Avant de revenir au code, il faut gnrer les classes depuis les objets crs avec linterface graphique.
Pour cela :
1. Cliquez sur lentit Entreprise et faites Files > New File.
2. Choisissez ensuite iPhone OS > Cocoa Touch Class > Managed Object Class.
3. Appuyez sur Next. Laissez le chemin propos par dfaut et cliquez encore sur Next.
4. Slectionnez Entreprise et Employe puis cochez seulement Generate accessors et Generate
Obj-C 2.0 properties. Vous devriez obtenir quatre fichiers : Entreprise.h, Entreprise.m,
Employe.h et Employe.m.
Arrtons-nous quelques instants sur ces fichiers. Vous remarquerez quil ny a pas de mthode
dealloc. Cest tout fait normal, Core Data se chargeant de grer les objets en mmoire, cela se fait
en interne.
De plus, en regardant Employe.h, il ne vous aura pas chapp que la variable age tait en fait un objet
NSNumber. En effet, Core Data gre des objets et non des types comme int ou oat.
Revenons maintenant notre application.Vous allez afficher dans une premire table view la liste des
entreprises. Pour cela, il faut crer une table view. Ajoutez un nouveau fichier UIViewController >
UITableViewController que vous nommerez TableRootViewController. Vous allez lafficher pardessus la window en modifiant les fichiers :

CoreDataAppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
@interface CoreDataAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
@private
NSManagedObjectContext *managedObjectContext_;
NSManagedObjectModel *managedObjectModel_;
NSPersistentStoreCoordinator *persistentStoreCoordinator_;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain, readonly) NSManagedObjectContext
*managedObjectContext;
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator
*persistentStoreCoordinator;
@property (nonatomic, retain) UINavigationController *navigationController;
- (NSString *)applicationDocumentsDirectory;

36

Utiliser Core Data

01_BlocN_iPhone.indd 351

351

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

352

Utiliser Core Data

@end
CoreDataAppDelegate.m
#import CoreDataAppDelegate.h
#import TableRootViewController.h
@implementation CoreDataAppDelegate
@synthesize window;
@synthesize navigationController;
#pragma mark #pragma mark Application lifecycle
-(BOOL)application:(UIApplication
*)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Congure le table view controller
TableRootViewController *rootViewController = [[TableRootViewController
alloc] initWithStyle:UITableViewStylePlain];
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Gestion de lerreur
}
// On passe le managed object context au controller
rootViewController.managedObjectContext = context;
UINavigationController *aNavigationController = [[UINavigationController
alloc] initWithRootViewController:rootViewController];
self.navigationController = aNavigationController;
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
[rootViewController release];
[aNavigationController release];
return YES;
}
// ne pas modier la suite
// mais dans le dealloc, release le navigationController
Il faut galement modifier votre TableRootViewController. Il va possder une instance managedObjectContext qui servira de passerelle jusqu Core Data ainsi quun bouton pour ajouter une entreprise. Modifier le .h :

#import <UIKit/UIKit.h>
@interface TableRootViewController : UITableViewController {
NSManagedObjectContext *managedObjectContext;
UIBarButtonItem *addButton;
}
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@end

01_BlocN_iPhone.indd 352

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ajoutez dans le .m :

@synthesize managedObjectContext;
Ensuite, dans le viewDidLoad, ajoutez un titre :

// Mettre le titre
self.title = @Entreprises;
Puis un bouton pour modifier la liste des entreprises :

// Mettre les boutons self.navigationItem.leftBarButtonItem = self.editButtonItem;


Et un autre pour ajouter une nouvelle entreprise :

addButton = [[UIBarButtonItem alloc]


initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:@selector(ajouterEntreprise)];
self.navigationItem.rightBarButtonItem = addButton;
ce stade, votre application devrait ressembler la Figure 36.5.

Figure 36.5 : Le dbut dune


grande aventure.

Pour donner notre entreprise, nous allons recourir une UIAlertView qui contient un champ de
texte. Pour cela, nous allons concevoir une subclass de UIAlertView que nous nommerons AlertViewTextField. Crez une nouvelle classe NSObject nomme AlertViewTextField.

AlertViewTextField.h
#import <Foundation/Foundation.h>
@interface AlertViewTextField : UIAlertView {
UITextField *theTextField;
}
@property (nonatomic, retain) UITextField *theTextField;

36

Utiliser Core Data

01_BlocN_iPhone.indd 353

353

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

354

Utiliser Core Data

@property (readonly) NSString *textEntered;


-(id)initWithTitle:(NSString *)title message:(NSString *)message
delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle
okButtonTitle:(NSString *)okButtonTitle;
@end
AlertViewTextField.m
#import AlertViewTextField.h
@implementation AlertViewTextField
@synthesize theTextField;
@synthesize textEntered;
-(id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle okButtonTitle:(NSString *)okButtonTitle
{
if (self = [super initWithTitle:title message:message delegate:delegate
cancelButtonTitle:cancelButtonTitle otherButtonTitles:okButtonTitle,
nil])
{
UITextField *aTextField = [[UITextField alloc]
initWithFrame:CGRectMake(12.0, 45.0, 260.0, 25.0)];
[aTextField setBorderStyle:UITextBorderStyleRoundedRect];
[aTextField setBackgroundColor:[UIColor clearColor]];
[self addSubview:aTextField];
self.theTextField = aTextField;
[aTextField release];
}
return self;
}
- (void)show {
// on rend le champ de texte rst responder, pour permettre automatiquement
dajouter du texte
[theTextField becomeFirstResponder];
[super show];
}
// retourne le texte entr
-(NSString *)textEntered {
return theTextField.text;
}
-(void)dealloc
{
[theTextField release];
[super dealloc];
}
@end

01_BlocN_iPhone.indd 354

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ensuite, de retour dans le TableRootViewController, dans la mthode ajouterEntreprise, vous allez


afficher une alert view personnalise. Noubliez pas de faire limport de AlertViewTextField.h.

-(void) ajouterEntreprise {
alertViewTextField *alert = [[AlertViewTextField alloc]initWithTitle:@Entreprise
message:@Ajouter une nouvelle entreprise
delegate:self
cancelButtonTitle:@Annuler
okButtonTitle:@Ajouter];
[alert show];
[alert release];
}
Lorsque lutilisateur annulera ou validera sa saisie, la mthode -(void)alertView:(UIAlertView *)
alertView willDismissWithButtonIndex:(NSInteger)buttonIndex sera appele. cet endroit, si
lutilisateur valide son choix, il faut ajouter une nouvelle entreprise.
Commencez par rcuprer le texte entr :

-(void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)


buttonIndex {
if (buttonIndex != [alertView cancelButtonIndex])
{
// on rcupre le texte entr
NSString *entered = [(AlertViewTextField *)alertView textEntered];
}
}
Ensuite, crez une nouvelle entreprise en ajoutant cette ligne, toujours dans la condition if :

// crer une nouvelle entreprise


Entreprise *entreprise = (Entreprise *)[NSEntityDescription
insertNewObjectForEntityForName:@Entreprise
inManagedObjectContext:managedObjectContext];

Info
Attention ne pas oublier vos imports de .h ! Entreprise.h et AlertViewTextField.h
La ligne ci-dessus demande Core Data dinsrer un nouvel objet et nous attribuons le bon nom
cette nouvelle entreprise par la suite :

// lui mettre le nom saisi


entreprise.nom = entered;
Vous allez maintenant sauver cette entreprise en ajoutant ces quelques lignes :

// sauvegarde NSError *error;


if (![managedObjectContext save:&error]) {
// Grer lerreur
}

36

Utiliser Core Data

01_BlocN_iPhone.indd 355

355

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

356

Utiliser Core Data

Pour afficher la liste des entreprises dans la table view, vous avez besoin dun tableau. Pour cela,
dclarez dans TableRootViewController.h

NSMutableArray *entrepriseArray;
que vous initialiserez dans le viewDidLoad. Ensuite, de retour la sauvegarde de la nouvelle entreprise,
vous allez grer lajout de la nouvelle entreprise tout en triant la liste par ordre alphabtique ! Ajoutez
cette ligne :
// ajout dune nouvelle entreprise dans le tableau
[entrepriseArray addObject:entreprise];
Triez cette liste avec NSSort. On demande trier par ordre alphabtique partir de la cl nom.

// on trie
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@nom
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[entrepriseArray sortUsingDescriptors:sortDescriptors];
[sortDescriptor release];
Puis, pour rendre le tout joli et anim, ajoutez la nouvelle entreprise dans la liste avec une petite
animation. Pour connatre son emplacement, il suffit de demander lindex de lobjet entreprise.

// on anime linsertion dune nouvelle ligne


NSIndexPath *indexPath = [NSIndexPath
indexPathForRow:[entrepriseArrayindexOfObject:entreprise] inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop animated:YES];
Pour finir, modifiez ces trois mthodes afin dafficher la liste des entreprises :

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Retourne le nombre de sections
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
// Retourne le nombre de lignes dans la section
return [entrepriseArray count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentier:CellIdentier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier]autorelease];

01_BlocN_iPhone.indd 356

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
Entreprise *entreprise = (Entreprise*)[entrepriseArray objectAtIndex:[indexPath row]];
cell.textLabel.text = entreprise.nom;
return cell;
}
Compilez, puis lancez votre application. Ajoutez des entreprises, elles doivent bien safficher !
Quittez puis relancez votre application. Attention bien la terminer pour ne pas quelle reste en tche
de fond (double appui sur la touche Home, puis appui long sur votre application et appui sur le sens
interdit). Zut, les donnes ne saffichent pas ! Core Data ne marcherait pas ? Jai fait tout a pour rien ?
Pas dinquitude, on va voir tout a...

RCUPRER

LA SAUVEGARDE DES DONNES

Nous avons vu comment modifier et ajouter une entreprise dans la base de donnes cre par Core
Data. Ces objets entreprise existent bien, il faut simplement les rcuprer au lancement de lapplication. Ainsi dans le viewDidLoad, demandons rcuprer les entits Entreprise.

NSFetchRequest *request = [[NSFetchRequest alloc] init];


NSEntityDescription *entity = [NSEntityDescription entityForName:@Entreprise
inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
Ensuite, trions les donnes, toujours par ordre alphabtique :

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@nom


ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
Puis excutons la requte pour rcuprer le tableau :

NSError *error;
NSMutableArray *mutableFetchResults =
[[managedObjectContextexecuteFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Gestion de lerreur
}
self.entrepriseArray = mutableFetchResults;
[mutableFetchResults release];
[request release];

Info
Vous remarquerez que lon fait une mutableCopy du tableau retourn par executeFetchRequest:error:.
En effet, cette mthode retourne un NSArray, alors que nous souhaitons un NSMutableArray...
Par ailleurs, noubliez pas de dclarer entrepriseArray en @property et @synthesize !

36

Utiliser Core Data

01_BlocN_iPhone.indd 357

357

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

358

Utiliser Core Data

Avant de continuer, faisons le point sur vos implmentations des mthodes dealloc. Jespre que vous
ne mavez pas attendu ! Voici les deux dealloc que vous devriez avoir :

CoreDataAppDelegate.m
-(void)dealloc {
[navigationController release];
[managedObjectContext release];
[managedObjectModel release];
[persistentStoreCoordinator release];
[window release];
[super dealloc];
}
TableRootViewController.m
-(void)dealloc {
[addButton release];
[managedObjectContext release];
[entrepriseArray release];
[super dealloc];
}
Avec larrive du multitche sous liOS 4, ce type dapplication o lon gre lcriture des donnes
est assez contraignant dboguer. En effet, votre application est suspendue par dfaut. Ce qui
signifie que le systme, dans la mesure du possible, garde ltat de lapplication comme lorsque vous
lavez quitt. Il est donc impossible de savoir si la persistance des donnes vient du multitche ou
de votre sauvegarde. Pour la quitter rellement, il faut donc appuyer deux fois sur le bouton Home,
puis la supprimer de lHistorique des ouvertures.
Pour viter ceci, vous pouvez ajouter cette cl dans le CoreData-Info.plist :
Application does not run in background et cocher la case qui apparat droite.

Info
Rappelez-vous quApple ne garantit pas la sauvegarde complte de ltat courant de votre application.
Il faut donc implmenter cette sauvegarde manuellement pour viter les mauvaises surprises.
Avant dajouter les employs dans les entreprises, nous allons grer ldition des entreprises, notamment leur suppression. Pour cela, implmenter ces deux mthodes. La premire est appele quand
lutilisateur glisse le doigt horizontalement sur la cellule puis appuie sur le bouton Supprimer. La
seconde, quant elle, est appele lorsque lutilisateur appuie sur le bouton diter :

#pragma mark
#pragma mark Edition
-(void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// On supprime lentreprise pointe par la cellule choisie par
lutilisateur pour la suppression

01_BlocN_iPhone.indd 358

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSManagedObject *entrepriseToDelete =
[entrepriseArrayobjectAtIndex:indexPath.row];

[managedObjectContext deleteObject:entrepriseToDelete];
// On met jour le tableau, ainsi que la table view.
[entrepriseArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
// On sauvegarde les changements
NSError *error;
if (![managedObjectContext save:&error])
{
// Gestion de lerreur
}
}
}
-(void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
// On dsactive le bouton dajout si on est en mode dition
self.navigationItem.rightBarButtonItem.enabled = !editing;
}
Arriv ce stade, je vous propose daller boire un verre, vous arer lesprit. Nous allons en effet voir
beaucoup de points. Le premier sera la gestion de ldition (ajout/suppression de cellules) et le suivant,
la cration dune table view pour permettre de crer de nouveaux employs.

A JOUTER

DES EMPLOYS

Commenons par crer un nouveau table view controller que vous nommerez TableEmployesViewController. Ce table view controller prsentera une liste des diffrents employs de lentreprise
slectionne. Il permettra lajout dun nouvel employ ou la suppression dun existant.
Lappel ce contrleur se fera aprs lappui dune ligne contenant le nom dune entreprise. Ainsi, dans
TableRootViewController.m, ajoutez cette mthode :

-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
TableEmployesViewController *viewController =[[TableEmployesViewController
alloc] initWithStyle:UITableViewStylePlain];
// on passe au contrleur lentreprise slectionne
viewController.entreprise = [entrepriseArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController:viewController
animated:YES];
[viewController release];
}

36

Utiliser Core Data

01_BlocN_iPhone.indd 359

359

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

360

Utiliser Core Data

Voici le .h de TableEmployesViewController :

#import <UIKit/UIKit.h>
@class Entreprise;
@interface TableEmployesViewController : UITableViewController {
UIBarButtonItem *addButton;
NSMutableArray *employesArray;
Entreprise *entreprise;
}
@property (nonatomic, retain) NSMutableArray *employesArray;
@property (nonatomic, retain) Entreprise *entreprise;
@end
Modifions tout dabord le viewDidLoad :

-(void)viewDidLoad {
[super viewDidLoad];
// on autorise la slection des cells mme pendant ldition (dans le cas o
lon autoriserait la modication dun employ)
//self.tableView.allowsSelectionDuringEditing = YES;
// Mettre le titre
self.title = entreprise.nom;
// Mettre le bouton dedition
self.navigationItem.rightBarButtonItem = self.editButtonItem;
// On initialise le tableau partir des employs de lentreprise
self.employesArray = [[[NSMutableArray alloc]
initWithArray:[entreprise.Employes allObjects]] autorelease];
}
Noubliez pas limport de Entreprise.h ainsi que les @synthezise.

Info
Attention, employes est un objet de la classe NSSet. Pour rcuprer un tableau des objets quil
contient, il faut donc utiliser allObjects.
Notez que si lon souhaitait rcuprer tous les employs stocks dans la base de donnes (juste audessus, nous rcuprons les employs de lentreprise slectionne) il faudrait procder comme suit :

NSManagedObjectContext *context = entreprise.managedObjectContext;


NSFetchRequest *request = [[NSFetchRequest alloc] init];
// Rcuprer tous les employs
NSEntityDescription *entity = [NSEntityDescription entityForName:@Employe
inManagedObjectContext:context];
[request setEntity:entity];
// Les trier par leur nom

01_BlocN_iPhone.indd 360

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@nom


ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
NSError *error;
NSMutableArray *mutableFetchResults = [[context executeFetchRequest:request
error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Gestion de lerreur
}
self.employesArray = mutableFetchResults;
[mutableFetchResults release];
[request release];
Nous allons nous occuper de ldition. Lide est la suivante : en mode normal, nous affichons la liste
des employs. En mode dition, nous affichons toujours cette liste, avec une ligne en plus qui nous
permettra dajouter un nouvel employ.

Figure 36.6 : La vue en mode normal.

36

Utiliser Core Data

01_BlocN_iPhone.indd 361

Figure 36.7 : La vue en mode dition.

361

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

362

Utiliser Core Data

Commenons par ajouter une ligne si lon est en mode dition :

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Retourne le nombre de sections
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
// Retourne le nombre demploys dans lentreprise, + 1 si on est en mode
dition pour la ligne dajout dun employ
// Trs important, sinon votre application plante en vous disant quil lui
manque des lignes ou quelle en a trop...
NSUInteger count = [employesArray count];
if (self.editing) {
count++;
}
return count;
}
Ensuite, il faut implmenter le comportement de lapplication lorsque lon appuie sur le bouton Edit.
Cela va nous permettre dajouter ou enlever la ligne avec le signe + et de sauver les changements :

// gestion de ltat lorsque lon touche le bouton diter (Edit ou Done)


-(void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
// On ne montre pas le bouton de retour lorsque lon est en mode dition
[self.navigationItem setHidesBackButton:editing animated:YES];
// On indique que lon va commencer les mises jour (suppression /ajout)
de lignes (ici, une ligne la fois)
[self.tableView beginUpdates];
// On rcupre le nombre demploys NSUInteger count = [employesArray count];
// On cr un tableau dindexPath (ici un seul objet sera prsent dans le
tableau)
NSArray *employeInsertIndexPath =
[NSArray arrayWithObject:[NSIndexPathindexPathForRow:count inSection:0]];
// On ajoute ou enlve la ligne
// En fait, cette ligne est celle qui contient le bouton + pour ajouter un employ !
// On lajoute lorsquon commence ldition, puis on lenlve lappui du bouton Done
UITableViewRowAnimation animationStyle = UITableViewRowAnimationNone;
if (editing) {
if (animated) {
animationStyle = UITableViewRowAnimationFade;
}
[self.tableView insertRowsAtIndexPaths:employeInsertIndexPath
withRowAnimation:animationStyle];
}

01_BlocN_iPhone.indd 362

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

else {
// on enlve toujours avec animation
[self.tableView deleteRowsAtIndexPaths:employeInsertIndexPath
withRowAnimation:UITableViewRowAnimationFade];
}
// les mises jour sur la table view sont nies
[self.tableView endUpdates];
// Si on a ni dditer, alors on sauve le context de Core Data
if (!editing) {
NSManagedObjectContext *context = entreprise.managedObjectContext;
NSError *error = nil;
if (![context save:&error]) {
// Gestion de lerreur
}
}
}
Pour afficher un joli bouton + et un bouton , implmentez cette mthode :

-(UITableViewCellEditingStyle)tableView:(UITableView
*)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
// La Ligne pour ajouter reoit un marqueur + lautre un marqueur -
if (indexPath.row == [employesArray count])
{
return UITableViewCellEditingStyleInsert;
}
return UITableViewCellEditingStyleDelete;
}
Ensuite, implmentons la mthode appele lorsque lon clique sur Delete ou lajout dun nouvel
employ. La suppression est la mme que celle vue dans TableRootViewController.

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingS


tyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// On rcupre le context
NSManagedObjectContext *context = entreprise.managedObjectContext;
if (editingStyle == UITableViewCellEditingStyleDelete) {
// On supprime la ligne du dataSource
// On cherche lemploy supprimer
Employe *employe = [employesArray objectAtIndex:indexPath.row];
// On supprime lemploy du context
[entreprise removeEmployesObject:employe];
// Notez que si un mme employ appartenait plusieurs entreprises,et
que lon souhaitait supprimer cet employ dnitivement de toutes les
entreprises, on ferait

36

Utiliser Core Data

01_BlocN_iPhone.indd 363

363

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

364

Utiliser Core Data

/* [context deleteObject:employe]; */
// On enlve lemploy du tableau, ainsi que la ligne qui lui correspond
[employesArray removeObject:employe];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
// On sauvegarde les changements
NSError *error = nil;
if (![context save:&error]) {
// Gestion de lerreur
}
}
else
if (editingStyle == UITableViewCellEditingStyleInsert) {
// On cre une nouvelle instance de Employ, que lon insert dans le
tableau et la table view aprs cration
[self insertEmployeAnimated:YES];
}
}
Pour linstant, laissons la mthode insertEmployeAnimated: vide :

-(void)insertEmployeAnimated:(BOOL)animated { }
Et noubliez pas de la dclarer dans le .h ! Affichons maintenant les employs, ainsi que le texte pour
la ligne dajout :
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = indexPath.row;
// On utilise ici une cellule par dfaut. On pourrait trs bien utiliser
une cellule par dfaut pour la ligne dajout dun Employ et une
personnalise pour lafchage des informations de lemploy, si lon
souhaitait insrer une photo par exemple
if (row == [employesArray count]) {
// afchage de la ligne qui permet lajout dun nouvel employ
static NSString *AddCellIdentier = @AddCell;
UITableViewCell *cell =
[tableViewdequeueReusableCellWithIdentier:AddCellIdentier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentier:AddCellIdentier] autorelease];
cell.textLabel.text = @Ajouter un employ;
}
return cell;
}
// afchage normal

01_BlocN_iPhone.indd 364

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

static NSString *NormalCellIdentier = @NormalCell;


UITableViewCell *cell =
[tableViewdequeueReusableCellWithIdentier:NormalCellIdentier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle
reuseIdentier:NormalCellIdentier] autorelease];
}
Employe *employe = (Employe*)[employesArray objectAtIndex:row];
cell.textLabel.text = [NSString stringWithFormat:@%@ %@,
employe.nom,employe.prenom];
cell.detailTextLabel.text = [NSString stringWithFormat:@%@ %d ans,
employe.sexe, [employe.age intValue]];
return cell;
}
Lorsque lon cre une nouvelle entreprise, elle na pas demploy. On prsente la vue pour lajouter
automatiquement :

-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([employesArray count] == 0) {
// Il ny a aucun employ dans lentreprise, donc on souhaite en ajouter un
[self setEditing:YES animated:NO];
// on lance la vue pour lajout de
lemploy
[self insertEmployeAnimated:YES];
}
}
Pour crer un nouvel employ, nous allons concevoir
une vue spcialement pour loccasion, rien que a ! Elle
ressemblera la Figure 36.8. Crez un nouveau table
view controller que vous nommerez TableEmployeDetailViewController.
Je ne dtaillerai pas le code ici car ce nest pas le propos.
Il est en outre suffisamment comment pour que vous
le compreniez.

Figure 36.8 : La vue dajout dun employ.

36

Utiliser Core Data

01_BlocN_iPhone.indd 365

365

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

366

Utiliser Core Data

Lessentiel est dutiliser un dictionnaire temporaire pour stocker les valeurs. Chaque cellule de notre table
view correspond un attribut de Employe. chaque attribut correspond un numro de ligne qui servira
galement comme cl dans le dictionnaire temporaire (0 correspond au nom, 1 au prnom...)
Par exemple :

tempValues : {
0 = Paul;
3 = 23;
1 = Marian;
2 = M;
}
Ces valeurs sont dfinies dans le .h :

#import <UIKit/UIKit.h>
@class Employe;
#dene kNumberOfEditableRows 4 // Nom, prnom, age et sexe
#dene kNameRowIndex 0
#dene kFirstNameRowIndex 1
#dene kSexRowIndex 2
#dene kAgeIndex 3
#dene kLabelTag 4096
@interface TableEmployeDetailViewController :
UITableViewController<UITextFieldDelegate> {
Employe *employe;
NSArray *eldLabels;
NSMutableDictionary *tempValues;
UITextField *textFieldBeingEdited;
}
@property (nonatomic, retain) Employe *employe;
@property (nonatomic, retain) NSArray *eldLabels;
@property (nonatomic, retain) NSMutableDictionary *tempValues;
@property (nonatomic, retain) UITextField *textFieldBeingEdited;
-(void)cancel:(id)sender;
-(void)save:(id)sender;
-(void)textFieldDone:(id)sender;
@end
Noubliez pas dans le .m les import et @synthesize :

#import TableEmployeDetailViewController.h
#import TableEmployesViewController.h
#import Employe.h
#import Entreprise.h
@implementation TableEmployeDetailViewController

01_BlocN_iPhone.indd 366

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

@synthesize
@synthesize
@synthesize
@synthesize
... ...
@end

employe;
eldLabels;
tempValues;
textFieldBeingEdited;

Commenons par le viewDidLoad et le dealloc :

-(void)viewDidLoad {
// On charge les valeurs des labels prcisants les noms des champs
NSArray *array = [[NSArray alloc] initWithObjects:@Nom :, @Prnom :,
@Sexe :,@Age :, nil];
self.eldLabels = array;
[array release];
// bouton annuler
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]
initWithTitle:@Annuler style:UIBarButtonItemStylePlain target:self
action:@selector(cancel:)];
self.navigationItem.leftBarButtonItem = cancelButton;
[cancelButton release];
// bouton sauvegarder
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithTitle:@Sauver
style:UIBarButtonItemStyleDone target:self action:@selector(save:)];
self.navigationItem.rightBarButtonItem = saveButton;
[saveButton release];
// dclaration du dictionnaire temporaire
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
self.tempValues = dict;
[dict release];
[super viewDidLoad];
}
-(void)dealloc {
[textFieldBeingEdited release];
[tempValues release];
[employe release];
[eldLabels release];
[super dealloc];
}
Ensuite, les mthodes cancel: et save: :

-(void)cancel:(id)sender{
// Dans le cas o lon annule, cela sous-entend que lon ne souhaite pas
sauvegarder lemploy
// On rcupre tous les contrleurs dans la navigation controller

36

Utiliser Core Data

01_BlocN_iPhone.indd 367

367

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

368

Utiliser Core Data

// En O : TableRootViewController
// En 1 : TableEmployesViewController
// En 2 : TableEmployeDetailViewController
NSArray *allControllers = [self.navigationController viewControllers];
TableEmployesViewController *parent = (TableEmployesViewController*)
[allControllers objectAtIndex:[allControllers count] - 2];
// avant dernier objet
Entreprise *entreprise = parent.entreprise;
// on enlve de lentreprise
[entreprise removeEmployesObject:employe];
// et du tableau
[parent.employesArray removeObject:employe];
[self.navigationController popViewControllerAnimated:YES];
}
-(void)save:(id)sender {
// Si on est en train dditer un text eld (on na pas appuy sur la touche
retour), alors on prend la valeur en cours que lon sauvegarde dans le
dictionnaire temporaire
if (textFieldBeingEdited != nil) {
NSNumber *tagAsNum= [NSNumber numberWithInt:textFieldBeingEdited.tag];
[tempValues setObject:textFieldBeingEdited.text forKey: tagAsNum];
[tagAsNum release];
}
// On parcourt les valeurs du dictionnaire temporaire pour setter
correctement lemploy
for (NSNumber *key in [tempValues allKeys]) {
switch ([key intValue]) {
case kNameRowIndex:
employe.nom = [tempValues objectForKey:key];
break;
case kFirstNameRowIndex:
employe.prenom = [tempValues objectForKey:key];
break;
case kSexRowIndex:
employe.sexe = [tempValues objectForKey:key];
break;
case kAgeIndex:
employe.age =
[NSNumber numberWithInt:[[tempValuesobjectForKey:key] intValue]];
break;
default:
break;
}
}

01_BlocN_iPhone.indd 368

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

// On pourrait sauvegarder ici. On ne le fait pas, car par choix,


TableEmployesViewController sen charge en appuyant sur Done

// Notez que si vous ajoutez plusieurs employs sans appuyer sur Done, et
que votre application crash, ces derniers ne seront pas sauvs. Pour
viter ceci, sauvez ici le context.

[self.navigationController popViewControllerAnimated:YES];
}
Ensuite, les mthodes qui servent laffichage de la table view :

// titre au dessus de la table view


-(NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
return @Dcrire le nouvel employ;
}
#pragma mark #pragma mark Table Data Source Methods
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return kNumberOfEditableRows;
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *EmployeCellIdentier = @EmployeCellIdentier;
UITableViewCell *cell =
[tableViewdequeueReusableCellWithIdentier:EmployeCellIdentier];
if (cell == nil) {
// construction de la cellule
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentier:EmployeCellIdentier] autorelease];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 95, 25)];
label.textAlignment = UITextAlignmentRight;
label.tag = kLabelTag;
label.font = [UIFont boldSystemFontOfSize:14];
[cell.contentView addSubview:label];
[label release];
UITextField *textField = [[UITextField alloc]initWithFrame:CGRectMake(110,
12, 180, 25)];
textField.clearsOnBeginEditing = NO;
[textField setDelegate:self];
[textField addTarget:self
action:@selector(textFieldDone:)forControlEvents:UIControlEventEditingDidEndOnExit];
[cell.contentView addSubview:textField];
}

36

Utiliser Core Data

01_BlocN_iPhone.indd 369

369

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

370

Utiliser Core Data

NSUInteger row = [indexPath row];


UILabel *label = (UILabel *)[cell viewWithTag:kLabelTag];
label.text = [eldLabels objectAtIndex:row];
// on cherche le text eld parmi les sous vues de la cell (label,textField, ...)
UITextField *textField = nil;
for (UIView *oneView in cell.contentView.subviews) {
if ([oneView isMemberOfClass:[UITextField class]])
textField = (UITextField *)oneView;
}
NSNumber *rowAsNum = [[NSNumber alloc] initWithInt:row];
switch (row) {
case kNameRowIndex: // Si le nom a dj t renseign, alors on le place
if ([[tempValues allKeys] containsObject:rowAsNum])
textField.text = [tempValues objectForKey:rowAsNum];
else // sinon, on choisit le nom par dfaut de employ lors de sa cration
textField.text = employe.nom; // on choisit si le text eld fait
appel au dictionnaire ainsi que le type du clavier
[textField setAutocorrectionType:UITextAutocorrectionTypeNo];
[textField setKeyboardType:UIKeyboardTypeDefault];
break;
case kFirstNameRowIndex:
if ([[tempValues allKeys] containsObject:rowAsNum])
textField.text = [tempValues objectForKey:rowAsNum];
else
textField.text = employe.prenom;
[textField setAutocorrectionType:UITextAutocorrectionTypeNo];
[textField setKeyboardType:UIKeyboardTypeDefault];
break;
case kAgeIndex:
if ([[tempValues allKeys] containsObject:rowAsNum])
textField.text = [tempValues objectForKey:rowAsNum];
else // attention, employe.age est un NSNumber
textField.text = [NSString stringWithFormat:@%d,
employe.age intValue]];
[textField setAutocorrectionType:UITextAutocorrectionTypeNo];
[textField setKeyboardType:UIKeyboardTypeNumberPad];
break;
case kSexRowIndex:
if ([[tempValues allKeys] containsObject:rowAsNum])
textField.text = [tempValues objectForKey:rowAsNum];
else
textField.text = employe.sexe;
[textField setAutocorrectionType:UITextAutocorrectionTypeNo];
[textField setKeyboardType:UIKeyboardTypeDefault];

01_BlocN_iPhone.indd 370

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

break;
default:
break;
}
// Par dfaut, on a plac par exemple Nom : dans le label, et Nom par
dfaut pour le nom de lemploy, on regarde donc si le text du label
raccourci de 2 lettre en partant de la n ( :) est gal au nom par dfaut.
// Si cest le cas, cest que lon doit modier le texte. Ainsi, on autorise
le texteld effacer son contenu lorsquon le choisit
if([[label.text substringToIndex:[label.text length]
- 2]isEqualToString:textField.text] || [textField.text isEqualToString:@0])
textField.clearsOnBeginEditing = YES;
else
textField.clearsOnBeginEditing = NO;
if (textFieldBeingEdited == textField) textFieldBeingEdited = nil;
textField.tag = row; [rowAsNum release];
return cell;
}
#pragma mark #pragma mark Table Delegate Methods
-(NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return nil; }
Et pour finir celles qui grent les text fields :

// Appel lorsque lon appuie sur la touche return du clavier


-(void)textFieldDone:(id)sender {
// On cherche la cell qui contient le text eld qui vient de nir dtre
dit pour retrouver ensuite le numro de la ligne correspondante
// Note : sender est ce textField
UITableViewCell *cell = (UITableViewCell *)[[sender superview] superview];
UITableView *table = (UITableView *)[cell superview];
NSIndexPath *textFieldIndexPath = [table indexPathForCell:cell];
NSUInteger row = [textFieldIndexPath row];
row++;
// si on est la n, on retourne au dbut ! un row%kNumberOfEditableRowsferait
galement laffaire
if (row >= kNumberOfEditableRows)
row = 0;
NSUInteger newIndex[] = {0, row};
NSIndexPath *newPath = [[NSIndexPath alloc] initWithIndexes:newIndex length:2];
// on cherche la cellule suivante ((row+1)%kNumberOfEditableRows)
UITableViewCell *nextCell = [self.tableView cellForRowAtIndexPath:newPath];
[newPath release];

36

Utiliser Core Data

01_BlocN_iPhone.indd 371

371

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

36

372

Utiliser Core Data

UITextField *nextField = nil;


for (UIView *oneView in nextCell.contentView.subviews) {
if ([oneView isMemberOfClass:[UITextField class]])
nextField = (UITextField *)oneView;
}
// on afche le clavier pour le prochain text eld
[nextField becomeFirstResponder];
}
#pragma mark Text Field Delegate Methods
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.textFieldBeingEdited = textField;
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
// On pourrait tester ici si lge est rel (< 60 par exemple) // De plus, un
test pourrait tre fait sur le sexe qui devrait tre M ou F
NSNumber *tagAsNum = [[NSNumber alloc] initWithInt:textField.tag];
[tempValues setObject:textField.text forKey:tagAsNum];
[textField setClearsOnBeginEditing:NO];
[tagAsNum release];
}
Ouf ! Une dernire petite chose faire, et vous pourrez enfin ajouter des employs votre entreprise ! En effet, il faut implmenter la mthode de TableEmployesViewController appele lorsque lon
choisit dajouter un nouvel employ !

-(void)insertEmployeAnimated:(BOOL)animated {
// on cre une nouvelle instance de Employe que lon ajoute dans Core Data
ainsi que dans le tableau
Employe *employe = [NSEntityDescriptioninsertNewObjectForEntityForName:@Employe
inManagedObjectContext:[entreprise managedObjectContext]];
// valeurs par dfaut
employe.nom = @Nom;
employe.prenom = @Prnom;
employe.sexe = @Sexe;
employe.age = [NSNumber numberWithInt:0];
// on ajoute lemploy lentreprise
[entreprise addEmployesObject:employe];
// Ajout dans le tableau et ajout dune ligne dans la table view
[employesArray addObject:employe];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[employesArraycount]
- 1 inSection:0];
UITableViewRowAnimation animationStyle = UITableViewRowAnimationNone;
if (animated) {
animationStyle = UITableViewRowAnimationFade;

01_BlocN_iPhone.indd 372

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

}
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:animationStyle];
// On push un nouveau table view controller pour diter les informations
de lemploy
TableEmployeDetailViewController *detailViewController =
[[TableEmployeDetailViewController
alloc]initWithStyle:UITableViewStyleGrouped];
detailViewController.employe = employe;
[self.navigationController
pushViewController:detailViewControlleranimated:YES];
[detailViewController release]; }
Noubliez pas cette ligne pour que tout fonctionne :

#import TableEmployeDetailViewController.h
Et le reload data pour afficher les bonnes donnes lorsque lon revient de ldition (avec le tri) :

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// on trie
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:@nom ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[employesArray sortUsingDescriptors:sortDescriptors];
[sortDescriptor release];
[self.tableView reloadData];
}

36

Utiliser Core Data

01_BlocN_iPhone.indd 373

373

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

374

Et du ct de liPad ?
37 Et du ct de liPad ?

Jusqu maintenant, nous navons pas spcifiquement parl diPad. Toutefois, vous devez
savoir que liPad fonctionne galement sous iOS.
Cet iOS est actuellement la version 3.2, mais la majorit de ce que nous avons appris jusquici peut se
transposer dans une application iPad. Regardons les principales diffrences.

CE

QUIL NY A PAS

LiPad nest pas un tlphone : vous naurez donc pas accs aux SMS ni la fonction tlphone.
Actuellement, lOS de liPad ne permet pas le multitche. Cependant, vous pouvez, en attendant la
mise jour du systme, utiliser le protocole NSCoding pour sauvegarder ltat courant de votre
application.

LiPad ne possde pas dappareil photo, ni larrire, ni lavant comme liPhone 4.

LES

PLUS

En dveloppant une application pour iPad, vous pourrez utiliser de nouvelles vues, parmi UISplitViewController (qui prsente deux vues en mme temps), UIPopoverController (qui prsente une petite
vue par-dessus les autres) et de nouvelles faons de prsenter des vues modales.

Avec liPad, votre application a la possibilit de partager des fichiers avec dautres applications.
Lcran est plus large (1024 768 pixels), plus de dtails pourront safficher !
UN

CRAN PLUS LARGE

Lcran de liPad est plus large, il faudra donc adapter les graphismes en fonction. En effet, il ne suffit
pas dagrandir les graphismes dune version iPhone, mais les repenser pour intgrer plus de dtails.
Lutilisateur de liPad, face son grand cran, sattendra bnficier dune application en haute dfinition.
De plus, qui dit cran plus large dit possibilit de placer plus dlments sur une mme vue. Nhsitez
donc pas proposer davantage de fonctionnalits !
Enfin, essayez de ne jamais changer toutes les vues de lcran en mme temps. En effet, lutilisateur de
liPhone a lhabitude dun petit cran qui ne peut pas tout afficher au mme endroit. Par contre, sur
iPad, comme lapplication Mail ou Bloc notes, vous pouvez placer une split view (UISplitViewController) qui prsente, par exemple, la fois une table view et son dtail.

LA

ROTATION

Il ny a pas de sens pour regarder liPad, cest mme rappel dans la publicit officielle... ce titre, il
est plus que prconis de rendre votre application apte prsenter ses donnes en portrait comme
en paysage. Pour cela, je vous conseille demployer Interface Builder pour construire vos vues, car il
sera plus facile dobserver les changements aprs vos rglages sans avoir recompiler lapplication
chaque fois.

01_BlocN_iPhone.indd 374

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Llment le plus important est la proprit autoresizingMask de la classe UIView. En changeant cette
proprit, vous modifierez le comportement de la vue lorsque liPad tourne. Voici les diffrentes
valeurs :

UIViewAutoresizingNone La vue ne sera pas modifie.


UIViewAutoresizingFlexibleLeftMargin La vue se redimensionnera en stirant (ou se rtrecissant) vers la gauche.

UIViewAutoresizingFlexibleRightMargin La vue se redimensionnera en stirant (ou se rtrcissant) vers la droite.

UIViewAutoresizingFlexibleTopMargin La vue se redimensionnera en stirant (ou se rtrcissant) vers le haut.

UIViewAutoresizingFlexibleBottomMargin La vue se redimensionnera en stirant (ou se rtrcissant) vers le bas.

UIViewAutoresizingFlexibleWidth La vue se redimensionnera en tirant (ou rtrcissant)


sa largeur.

UIViewAutoresizingFlexibleHeight La vue se redimensionnera en tirant (ou rtrcissant) sa


hauteur.
Sous Interface Builder, dans longlet Size dune vue, vous
pourrez choisir ces proprits dun simple clic en
observant travers lanimation le comportement. la
Figure 37.1, il est spcifi que la vue va tre ancre en
haut et gauche. Dans le code, cela correspondrait

UIViewAutoresizingFlexibleBottomMargin
UIViewAutoresizingFlexibleRightMargin.

Figure 37.1 : Utilisez lautoresizing.

Info
Les valeurs du type numres UIViewAutoresizing ne sont pas dfinies par hasard. En fait, la valeur
est un entier, mais que lon pourrait reprsenter en binaire :

enum {
UIViewAutoresizingNone
= 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth
= 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin
= 1 << 3,
UIViewAutoresizingFlexibleHeight
= 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 375

375

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

376

Et du ct de liPad ?

Ainsi, UIViewAutoresizingNone correspond 00000000b, UIViewAutoresizingLeftMargin


00000001b, UIViewAutoresizingFlexibleWidth 00000010b, ...
De ce fait, vous pouvez spcifier plusieurs paramtres en appliquant loprateur OU ( | ), obtenu
avec le raccourci clavier A+a+*+l. Par exemple, en faisant uneView.autoresizingMask
= UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth;, la valeur de
autoresizingMask sera de 00000011b, soit 3 en dcimal.
Enfin, votre application iPad est susceptible dtre lance en mode portrait ou paysage. Il est possible
de dfinir la bonne image au chargement en spcifiant Default-Portrait.png et Default-Landscape.png.
Figure 37.2, vous retrouvez une capture cran des images de chargement et licne comprenant les
fichiers pour iPad, iPhone et iPhone 4. De plus, les images sont localises (changent selon la langue de
lappareil).

iPad

iPhone

Figure 37.2 : Les images de chargement.

iPhone4

CRER

UNE APPLICATION UNIVERSELLE

Maintenant, vous avez le choix :

si vous souhaitez distribuer votre application seulement sur iPad, alors la question ne se pose pas ;
si vous souhaitez distribuer votre application sur les deux plates-formes, vous pouvez crer un
mme projet pour une application dite universelle. Ainsi, lutilisateur nachtera quune version
quil pourra lancer sur ces deux appareils sil les possde ;

vous dcidez de crer deux applications. Lavantage est que lutilisateur dun appareil seul aura une
application plus lgre tlcharger et conserver. Par contre, sil possde un iPhone et un iPad, il
devra payer deux fois lapplication, ce qui est trs frustrant pour lui.
Finalement, cest un dilemme : proposer une application universelle, pour satisfaire lutilisateur, ou en
vendre deux et esprer une rmunration plus juste face au travail fourni pour la deuxime version.

01_BlocN_iPhone.indd 376

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Noubliez pas que le patron de conception MVC (Model View Controller) prend tout son sens ici.
En effet, la vue pour liPad sera diffrente de celle pour liPhone, le contrleur sera donc diffrent lui
aussi car il ne grera pas les mmes lments dinterfaces. Par contre, les donnes restent les mmes !
Voici deux conseils pour grer les diffrences dans une mme classe :

Pour charger les lments dinterface selon lappareil, utilisez UI_USER_INTERFACE_IDIOM(). Par
exemple :

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
// on prsente linterface pour liPhone
}
else
{
// dans le cas o il ny a pas que liPad o liPhone (si dans un futur
proche un nouvel appareil sort...
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
// interface iPad
}
}

Pour utiliser une fonctionnalit non disponible sur les deux plates-formes, testez si la classe existe.
Par exemple :

if(NSClassFromString(@UIPopoverController))
{
// on peut afcher la popover
}
Nous allons voir quelques prmices (pour comprendre le mcanisme dune application universelle)
en ralisant une application qui affichera une liste dingrdients. Pour plus de dtails concernant les
applications universelles, rfrez-vous lexemple TopPaid disponible dans la documentation.
Crez un nouveau projet de type Window-based Application, universelle (Figure 37.3) que vous
nommerez UniversalApp.
louverture du projet, vous remarquerez que vous avez plusieurs application delegate et
MainWindow.xib, un pour liPhone et un pour liPad (Figure 37.4).
Nous allons commencer par nous occuper de la partie iPad. Pour cela, ouvrez le fichier MainWindow_
iPad.xib, puis ajoutez un nouveau contrleur UISplitViewController (Figure 37.5).

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 377

377

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

378

Et du ct de liPad ?

Figure 37.3 : Un nouveau


projet universel.

Figure 37.4 : Un fichier par plate-forme.

Figure 37.5 : Ajoutez un


split view controller.

01_BlocN_iPhone.indd 378

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ensuite, retournez sous Xcode puis dans AppDelegate_iPad.h et dclarez le contrleur :

#import <UIKit/UIKit.h>
@interface AppDelegate_iPad : NSObject <UIApplicationDelegate> {
UIWindow *window;
UISplitViewController *splitViewController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UISplitViewController *splitViewController;
@end
Dans le .m, ajoutez la vue la fentre :

@synthesize window;
@synthesize splitViewController;
#pragma mark #pragma mark Application lifecycle
- (BOOL)application:(UIApplication
*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
return YES;
}
Comme vous le voyez Figure 37.6, un split view controller affiche deux vues. Crons-les en ajoutant
un nouveau fichier de type UITableViewController, nomm RootViewController comme la
Figure 37.7 (pour la vue de gauche) et un UIViewController nomm DetailViewController comme
la Figure 37.8.

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 379

379

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

380

Et du ct de liPad ?

Figure 37.6 : Un split view controller.

Figure 37.7 : Crez un fichier de


type table view controller.

Figure 37.8 : Crez un nouveau


contrleur.

01_BlocN_iPhone.indd 380

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Ouvrez de nouveau le fichier MainWindow_iPad.xib, puis connectez le contrleur comme la


Figure 37.9.

Figure 37.9 :
Connectez le split
view controller.

Ensuite, droulez le split view controller dans la fentre MainWindow_iPad.xib, puis cliquez sur le view
controller. Dans linspecteur, onglet Identity, spcifiez DetailViewController comme la Figure 37.10.
Puis, dans longlet Attributes, spcifiez le nom du fichier nib (Figure 37.11).

Figure 37.10 : Changez le


nom de la classe pour
DetailViewController.

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 381

381

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

382

Et du ct de liPad ?

Figure 37.11 : Spcifiez le


fichier NIB charger.

Droulez ensuite le navigation controller pour trouver le table view controller. Dans longlet Identity,
spcifiez comme classe RootViewController (Figure 37.12).

Figure 37.12 : Changez le


nom de la classe pour
RootViewController.

Sauvez vos modifications, puis retournez dans Xcode et dans RootViewController.h. Nous allons
dclarer un tableau que nous initialiserons avec une liste de lgumes afficher.

#import <UIKit/UIKit.h>
@interface RootViewController : UITableViewController {
NSMutableArray *arrayToDisplay;
}
@end

01_BlocN_iPhone.indd 382

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Dans le .m, initialisez le tableau :

- (void)viewDidLoad {
[super viewDidLoad];
self.title = @Ratatouille;
arrayToDisplay =
[arrayToDisplay
[arrayToDisplay
[arrayToDisplay
[arrayToDisplay

[[NSMutableArray alloc] init];


addObject:@Tomate];
addObject:@Courgette];
addObject:@Aubergine];
addObject:@Poivron];

}
Puis modifiez ces mthodes :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [arrayToDisplay count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentier = @Cell;
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentier:CellIdentier];

if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentier:CellIdentier] autorelease];
}
cell.textLabel.text = [arrayToDisplay objectAtIndex:indexPath.row];
return cell;
}
Pour finir avec la partie iPad, nous allons afficher la slection sur la vue du contrleur DetailViewController. Pour cela, dans DetailViewController.h, dclarez un label :

#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController {
UILabel *detailLabel;
}
@property (nonatomic, retain) IBOutlet UILabel *detailLabel;
@end

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 383

383

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

384

Et du ct de liPad ?

Ajoutez dans le .m :

@implementation DetailViewController
@synthesize detailLabel;
Ensuite, rendez-vous dans DetailViewController.xib, puis ajoutez un label. Reliez-le comme la Figure 37.13.

Figure 37.13 :
Reliez le label.

Sauvez et quittez, puis, dans RootViewController.h, nous allons dclarer un objet de la classe Detail-

ViewController.
#import <UIKit/UIKit.h>
@class DetailViewController;
@interface RootViewController : UITableViewController {
NSMutableArray *arrayToDisplay;
IBOutlet DetailViewController *detailViewController;
}
@end
Dans le .m, affichez dans le label le lgume slectionn :

#import RootViewController.h
#import DetailViewController.h
@implementation RootViewController
// Laisser tel quel
#pragma mark #pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

01_BlocN_iPhone.indd 384

18/08/10 12:49

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

detailViewController.detailLabel.text =
[arrayToDisplay objectAtIndex:indexPath.row];

[detailViewController.detailLabel sizeToFit];
}
Ouvrez le fichier MainWindow_iPad.xib, puis cliquez sur le root ViewController et dans longlet
Connections, reliez le detailViewController comme la Figure 37.14.

Figure 37.14 : Reliez le


detailViewController.

Compilez, puis lancez votre application sous le simulateur iPad (voir Figure 37.15) pour observer le
rsultat !

Figure 37.15 : Rglez pour


lancer le simulateur iPad.

Maintenant, pour que notre application fonctionne galement avec liPhone, modifiez le fichier AppDelegate_iPhone.h pour dclarer un contrleur de navigation :

#import <UIKit/UIKit.h>
@interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 385

385

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

386

Et du ct de liPad ?

UIWindow *window;
UINavigationController *navigationController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController
*navigationController;
@end
Affichez-le dans le .m :

@implementation AppDelegate_iPhone
@synthesize window;
@synthesize navigationController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
Ouvrez ensuite le fichier MainWindow_iPhone.xib, puis ajoutez un contrleur de navigation comme
la Figure 37.16

Figure 37.16 : Crez un


contrleur de navigation.

Changez la classe de son contrleur (Figure 37.17). Ensuite, ajoutez un nouveau contrleur de vue
(Figure 37.18), modifiez sa classe pour DetailViewController (Figure 37.19) et changez le fichier nib
(Figure 37.20). Enfin, reliez le detailViewController du RootViewController comme la Figure 37.21.

01_BlocN_iPhone.indd 386

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 37.17 :
Changez la classe pour
RootViewController.

Figure 37.18 : Ajoutez un


nouveau UIViewController.

Figure 37.19 :
Changez la classe pour
DetailViewController.

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 387

387

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

388

Et du ct de liPad ?

Figure 37.20 : Changez


le fichier nib.

Figure 37.21 : Reliez le


detailViewController.

Pour afficher le dtail du lgume slectionn, il faut faire un push sur le detailViewController. Pour
cela, modifiez le RootViewController.m :

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath


*)indexPath {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
// on prsente linterface pour liPhone
[self.navigationController pushViewController:detailViewController
animated:YES];
detailViewController.detailLabel.text =
[arrayToDisplay objectAtIndex:indexPath.row];
}

01_BlocN_iPhone.indd 388

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

else
{
// interface iPad
detailViewController.detailLabel.text =
[arrayToDisplay objectAtIndex:indexPath.row];
}
[detailViewController.detailLabel sizeToFit];
}
Voil, maintenant votre application se lance aussi bien sur iPhone que sur iPad !

Info
Pour faire tourner le simulateur, appuyez sur Cmd+flche droite ou gauche.

UTILISER

UN SPLIT VIEW CONTROLLER

Pour crer un split view controller, rfrez-vous la section prcdente. Vous pouvez aussi choisir
de crer un nouveau projet de type Split View-based Application. Faites-le, et nommez-le PlayWithiPad.
En lanant lapplication, vous aurez directement un affichage de type split view.Vous remarquez quen
mode portrait, la liste est disponible via le bouton nomm Root List. En appuyant sur ce bouton une
popover saffichera avec la liste.

CRER

UNE POPOVER

Une popover est trs pratique pour afficher des informations. Autant que faire se peut, essayez de ne
pas inclure de bouton de type Done pour enlever la popover et enlevez-le aprs une action comme
un clic sur une cellule.
Laffichage dune popover se fait soit depuis un bouton dune barre, soit depuis un rectangle spcifi.
Pour lafficher depuis un bouton dans une vue, utilisez cette mthode :

- (IBAction) displayPopoverWithList : (id) sender {


UIButton *buttonSender = (UIButton*)sender;
UIPopoverController *popover = [[UIPopoverController alloc]
initWithContentViewController:listViewController];
popover.delegate = self;
// on fait un retain dessus pour pouvoir le rutiliser
self.popoverController = popover;
[popover release];
[self.popoverController presentPopoverFromRect:buttonSender.frame inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 389

389

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

390

Et du ct de liPad ?

Vous pouvez galement choisir la direction daffichage de la flche de cette popover. Ici, nous laissons
le choix au systme avec UIPopoverArrowDirectionAny.
Pour afficher une action sheet dans une popover, comme la Figure 37.22, il faut utiliser :

- (IBAction) displayPopoverWithActionSheet : (id) sender {


UIButton *buttonSender = (UIButton*)sender;
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:@Bouton 1, @bouton 2, nil];
[actionSheet showFromRect:buttonSender.frame inView:self.view animated:YES];
[actionSheet release];
}

Figure 37.22 : Une action sheet


dans le popover !

AFFICHER

UNE VUE MODALE

Vous pouvez changer la faon dafficher une vue modale en changeant la proprit modalPresentationStyle dun contrleur.Vous aurez le choix entre prsenter en plein cran (Figure 37.23), comme
une page (Figure 37.24), ou comme une vue au-dessus de toutes les autres (Figure 37.25).

Figure 37.23 : Une vue modale


en plein cran.

01_BlocN_iPhone.indd 390

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Figure 37.24 : Une vue modale


affiche comme une page.

Figure 37.25 : Une vue modale


au-dessus de la pile.

Voici le code pour y arriver :

- (void) loadModalViewController {
ModalViewController *controller = [[ModalViewController alloc]
initWithNibName:@ModalViewController bundle:nil];
self.modalVC = controller;
[controller release];
}
- (IBAction) displayModalViewFullScreen {
if (!modalVC) {
[self loadModalViewController];
}

37

Et du ct de liPad ?

01_BlocN_iPhone.indd 391

391

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

37

Et du ct de liPad ?

392

modalVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController:modalVC animated:YES];
}
- (IBAction) displayModalViewFormSheet {
if (!modalVC) {
[self loadModalViewController];
}
modalVC.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:modalVC animated:YES];
}
- (IBAction) displayModalViewPageSheet {
if (!modalVC) {
[self loadModalViewController];
}
modalVC.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController:modalVC animated:YES];
}

01_BlocN_iPhone.indd 392

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

LEXIQUE

01_BlocN_iPhone.indd 393

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

394

Lexique

Lexique
Allocation/dsallocation. Lallocation dun objet consiste demander au systme de rserver un
emplacement mmoire pour contenir les donnes de lobjet.
La dsallocation est le processus inverse o lon libre lemplacement. Reportez-vous la Fiche 32
pour vous familiariser avec lutilisation de la mmoire.
Application delegate. Lapplication delegate (ou dlgu de votre application) est une classe qui
lance votre application et affiche la fentre principale
atomic/non atomic. Paramtres qui spcifient le comportement de lobjet dans le thread.
En spcifiant nonatomic pour un objet, vous prcisez que votre objet sera susceptible dtre modifi
par plusieurs threads en mme temps (lobjet pourrait alors tre corrompu en cas de mauvaise
gestion de votre part).
linverse, utiliser atomic permet de bloquer la ressource pour ntre utilise que par le thread
appelant. Utiliser atomic permet de conserver lintgrit des donnes, mais ralentit laccs la
ressource et est inutile si vous nutilisez pas de threads.
Par ailleurs, vous pouvez utiliser nonatomic mme en utilisant plusieurs threads lorsque votre code
est crit de manire grer les ressources correctement.
Voir section Coder quelques lments simples.
Autocompltion. Lautocompltion (ou remplissage automatique) est un outil intgr Xcode qui
permet de proposer la fin des mots que vous crivez dans votre code. La gain de temps est trs
apprciable et permet de ne pas se tromper sur le nommage.
Bassin dautorelease. Un bassin dautorelease est un terme technique relatif la gestion mmoire.
Les objets placs dans ce bassin seront librs un instant t, au lieu dtre librs immdiatement avec
lappel release
Bundle/main bundle. Le main bundle est le dossier de votre application o sont placs lexcutable
et les ressources (images, fichiers...).
Dealloc. La mthode dealloc est relative la gestion mmoire. Cette mthode est appele lorsquun
objet est libr de la mmoire.
Device. Terme anglais qui signifie dispositif. Il dsigne lappareil : iPhone, iPod Touch ou iPad.
Getter/Setter. Get et Set sont respectivement traduits par rcuprer et mettre. En programmation
objet, le fait de setter un objet lui attribue une nouvelle valeur. linverse, getter permet de rcuprer
sa valeur sans modifier lobjet.
Exemple : on peut donc continuer notre initialisation en settant la proprit studentName. signifie
que lobjet studentName va avoir une nouvelle valeur. Voir Fiche 32.
include. Linclude est une directive pr-processeur qui permet dindiquer au compilateur dutiliser le
fichier spcifi lors de la compilation. Elle est trs peu utilise en Objective-C et remplace par
import.
Release. La mthode release permet lors de son appel, de dcrmenter le compteur de rfrence
dun objet. Une fois ce compteur de rfrence nul, lemplacement mmoire de lobjet est libr.
Exemple :il faut release backgroundImageView (dcrmenter son retain count de 1) signifie que vous
allez diminuer (en quelque sorte) son empreinte mmoire.

01_BlocN_iPhone.indd 394

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Parser. Un parser, ou analyseur syntaxique, permet danalyser un texte (en programmation, sous
forme de XML ou JSON) et crer une liste de donnes partir des balises trouves.
Mode debug/release/distribution. Ce sont les trois modes de compilation utiliss par Xcode.Voir
section Introduction Xcode.
Provisioning. Un fichier, cr par Apple, ncessaire pour compiler sur un appareil. Ce fichier permet
de signer lapplication.
Push. Le Push est un service de notification mis en place par Apple qui permet votre application de
recevoir des applications dun serveur distant nimporte quel instant.
Il ne faut le confondre avec pushViewController : une mthode qui permet dafficher un contrleur
de vue depuis un contrleur de navigation. Typiquement, vous appellerez cette mthode lorsque lutilisateur touchera une cellule.
Regex. Regex pour Regular expression est en informatique une chane de caractres qui dcrit un
ensemble de caractres possibles. Un test Regex peut par exemple permettre de vrifier la prsence
dun caractre ou dune expression dans une autre chane de caractres.
Exemple : faire un regex sur le NSString permet de vrifier que cet objet comporte bien certains
caractres comme la prsence de @ dans une adresse mail.
Retain count. Le retain count, pour compteur de rfrence, est une variable dinstance permettant
de connatre le nombre de rfrences mmoire dun objet. Par exemple, lorsque vous faites un retain
sur un objet, vous incrmentez son compteur de rfrence de 1.Voir Fiche 32.
Sender. Le sender est typiquement lobjet appelant dune mthode relative une action. Si vous
affectez les actions de plusieurs boutons sur la mme mthode, le sender va vous permettre de savoir
quel bouton appelle la mthode. Une phrase comme On fait une rfrence sur le sender, que lon
caste en UIButton, signifie que vous pourrez utiliser le bouton lorigine de lappel.
Switch. Le Switch est un interrupteur (dans le cas des lments dinterface UISwitch) et propose
une interaction avec lutilisateur en mode marche/arrt (on/off).
Switch case. En programmation, le switch case permet de faire plusieurs tests de valeurs sur le
contenu dune mme variable de type discrte (exemple : entier).
SDK. SDK pour Software Development Kit est un kit de dveloppement logiciel. Il contient des outils
qui permettent aux dveloppeurs de programmer pour la plate-forme spcifie.
Table view. Une table view est une liste de donnes couramment utilise pour prsenter des
tableaux dobjet.Vous la trouverez par exemple dans liPod montrant la liste de vos morceaux.
Templates. Ce sont des modles de projet qui constituent le point de dpart de votre application.
Thread. Un thread est un sous-processus. Il est souvent traduit en franais par fil dexcution. Les
threads permettent dexcuter des fonctions en parallle dans un mme processus. Reportez-vous au
document de la documentation Apple Threading Programming Guide.
Exemple : animer lActivity indicator view sur le thread principal signifie que vous allez lancer lanimation de la roue depuis le thread principal, celui qui est utilis par dfaut.
Warnings de compilation. Dans Xcode, ils signalent en jaune une erreur de compilation non
critique. Ces warnings nempcheront pas la compilation de votre excutable, mais peuvent provoquer des plantages ds les premires secondes.Vrifiez-les toujours !

Lexique

01_BlocN_iPhone.indd 395

395

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Index
Index

A
ABPeoplePickerNavigationController 252
ABPeoplePickerNavigationControllerDelegate
256
Acclromtre 112
Accesseurs 298
Achat 164
ADBannerViewDelegate 280
addSubview 195
Afficher
carte 92
publicit 281, 282
tab bar 212
alert view 66
Alloc 297
Analyseur statique 305
Animation
arrter 229
chemin 230
Core Graphics 235
couleurs 226
courbe de vitesse 230
enchanement 230
flip 21, 199
groupe 238
haut niveau 224
lancer 227, 228
rpter 227
transparence 227
UIModalTransition 204
UIViewAnimations 225
vue 201
zoom 230

Index

01_BlocN_iPhone.indd 397

Annotations 95
API
facebook 330
mto 330
twitter 330
Appareil 123
Apple ID 147
Application
traduire 287
universelle 376
App Store 3
distribuer 4
marketing 5
vente 164
Assign 25, 64
Asynchrone
GET 249
mthode 250
POST 250
atomic 25, 64, 300
autocorrection 40
Autorelease 297, 300
bassin 301, 328
autoresizingMask 375

B
Barre de navigation 21, 184
Bassin dautorelease 43, 301, 328
Blocks 117
Bluetooth 123
Boussole 100

397

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

398

Index

Bouton 39
connexion/dconnexion 123
tats 40
vnement 50
nuage 81
playPauseButton 108
Start/Stop 220
trembler 229
UIButton 40
Build and run 22

C
Cacher, barre de statut 157
CALayer 229
Carnet dadresses 252
contact 255
e-mail 265
liste 252
SMS 264
Carte 89
affichage 92
annotations 95
position 38
Catgories 60
Cellule
cliquer 73
personnalise 75
Certificat
cl 149
cration 10
dveloppement 148
openss1 151
pem 151
production 148
serveur 149
CGRect 222
Clavier
rtracter 57
spcifier 40

01_BlocN_iPhone.indd 398

Client 133, 134


CLLocationCoordinate2D 93, 96
CLLocationManager 100, 104, 116
Collision 220
Communiquer
appareils 123
serveur 117
sites web 188
Compilation
mode 22
simulateur 30
Compte de test 181
Compteur de rfrence 298
Connexion
deconnexion 123
internet 125
locale 125
Contact
choisir 256, 257
proprits 254
Contenu payant 164
Copy 25, 64, 297
Core Animation 199, 224
Core Data 348
Core Graphics 235
CoreLocation 100
Correction Voir Orthographe
@class 190

D
Dealloc 26
implmentation 298
Delegate 60, 216
mthodes 56, 85
naviguer 201
protocole 60
Dlgu 63
mthode 246, 250

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Documentation 14
aide contextuelle 14
autocompltion 16
en ligne 14
forums 17
Objective-C 7
Donnes
archiver 343
base 310
lecture 313
dsarchiver 344
changer 128
liste 70
sauvegarder 340, 357
transmettre 127
trier 356

ragir 48
rcuprer 268
secousse 162
supprimer 276
touchUpInside 85
EventKit 268

E
cran
haute dfinition 374
iPad 374
plein 146
publicit 282
rotation 374
Effacer 162
EKCalendar 268
EKEvent 270
EKEventStore 269
EKEventViewController 277
E-mail 265
vnement
afficher 273
ajouter 274
bouton 50
contrleur 268
delegate 60
envoyer 63
First responder 27
modifier 277

Index

01_BlocN_iPhone.indd 399

Fichier
implmentation 23
nib 22
plist 340
xib
Files owner 27
localiser 291
MainWindow 23
nib 22
view 191
Files owner 27
Filtre 114
First responder 27
Framework
CoreLocation 96, 100
json 331
Full screen 146

G
GameKit 123
Gateway 151
Geocoding 92, 94
GET 246
asynchrone 249
synchrone 248
getter 25, 64
GKPeerPickerController 123

399

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

400

Index

GPS 89
adresse 92, 93
altitude 104
Map View 38
prcision 103
vitesse 104
Grand Central Dispatch 117
Guide
marketing 5
Objective-C 13
programmation iPhone 13

I
iAd 278
activer 282
IBOutlet 25, 33, 298
id 63, 125
UDID 133
Image
afficher 157
ajouter 44
Interface Builder 46
album 109
modifier 159
par-dessus 160
sauvegarder 161
view 35
animation 36
Implmentation
fichiers 23
SQLManager 313
In App Purchase 164
Indicateur dactivit 42
Informer 147
init 297
insertSubview
atIndex 194
Interface Builder 27
Interface graphique 310

01_BlocN_iPhone.indd 400

Interrupteur 53
iPad 374
iPod
bibliothque 105
boussole 103
GPS 89
musique 105
iTunes Connect 9, 178

J
Jouer
musique 105
vido 140
JSON 330

L
label multiligne 103
Langage
C 6
Objective-C 6
objet 6
transition
depuis C++ 7
depuis Java 7
Langue
client 287
traduire 291
Leaks 308
Licence
achat 8
App Store 4
frais 8
gratuite 8
programme
entreprise 8
standard 8

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

Liste
carnet dadresses 252
circulaire 77
donnes 70
cliquer 73
style 71
JSON 336
Localisation
adresse 92, 93
altitude 92
GPS 89
latitude 93
longitude 93
MKUserLocation 89
prcision 92, 94, 103
punaise 96
Localizable.strings 290
localizedDescription 93

M
MapKit 38
Map View 38
Marketing
description 5
guide 5
mot-cl 5
Mmoire 296
fuite 300
gestion 296
Mthode
appel rgulier 221
asynchrone 250
classe 241
init 297
optionnelle 56, 60
requise 56, 60
MKAnnotation 95, 96
MKAnnotationView 95
MKCoordinateSpan 93
MKMapView 89, 92

Index

01_BlocN_iPhone.indd 401

MKReverseGeocoder 92
MKUserLocation 89
Mode
dition 359
paysage 376
portrait 376, 389
Mot-cl
@class 190
IBAction 52
IBOutlet 25, 33, 298
marketing 5
@optional 64
#pragma mark 66
Motion 163
Multitche 115
Multitouch 160
Musique 105
information 109
lecteur 108
morceaux 108
MVC 348

N
Navigation
bare 207
contrleur 201
pile 207
vue 201
nil 199
Nombre
ligne 72, 273
section 72
nonatomic 25, 64, 300
Notifications 115
locales 119
Push 147
NSArray 68, 194, 221
NSAutoreleasePool 301
NSCoding 122
NSData 127

401

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

402

Index

NSDate 122
NSDictionary 340
NSError 93
NSIndexPath 72
NSInteger 68
NSLocale 294
NSLocalizedString 290, 291
NSLog 50
NSMutableArray 68, 317
NSMutableString 104
NSNumber 68, 96
NSObject 68, 167, 241, 313, 353
NSOperationQueue 117
NSPredicate 57
NSRunLoop 223
NSSet 272
NSSort 356
NSTimer 219
NSURL 329
NSURLConnection 247
connectionDidFinishLoading 251
didFailWithError 250
didReceiveData 250
NSURLRequest 248
NSUserDefaults 343
NSValue 68
NSXMLParser 323
NSZombie 309

O
Objective-C 6
multitche 113
Objet 131
Onglet
badge 218
image 217
ordre 217
personnaliser 217
slectionner 214
titre 217

01_BlocN_iPhone.indd 402

OpenGL 21
Openss1 151
Ordinateur
certificat 12
gnration 6
simulateur 6, 303
Outil
analyse statique 305
Interface Builder 27
Leaks 308
Xcode 7, 21
@optional 64

P
Paiement 167
archivage 177
grer 174
Parser
JSON 323
XML 323
Patron de conception 68, 167
MVC 348
Photo 156
afficher 156
dessiner 156
enregistrer 156
sauvegarder 156
Pile de vue
ajouter 194
dplacer 194
popover 389
pop up 66
Position
Afficher 89
bannire 278
carte 38
CoreLocation 100
Map View 38
MKMapKit 89
MKMapView 92
utilisateur 89

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

POST 246
asynchrone 250
synchrone 249
Produit
Consumable 179
Non-consumable 179
Subscription 179
Programme
entreprise 8
standard 8
Protocole
ADBannerViewDelegate 280
delegate 60
dlgu 63
MKAnnotation 95
utiliser 64
Provisioning
crer 164
grer 12
portail 147
Publicit 278
afficher 281, 282
#pragma mark 66
@property 64, 190, 298, 300, 357

Q
Quartz Core 229

R
Release 22, 26, 297
auto 300
bassin 43
reloadData 326
removeFromSuperview 198
Rseau 242

Index

01_BlocN_iPhone.indd 403

Ressources
documentaires 13
forums spcialiss 6, 17
gestion 285
localiser 294
performance 4
projet 23
Retain 25, 45, 64, 297
RootViewController 189
@required 56, 64

S
Sandbox 153
Scroll view 81
taille 84
Secousse, dtecter 163
Sender 50
sendSynchronousRequest 248
Serveur 133
certificat 149
programmation 152
store 164
variables 241
setter 25, 64
Simulateur
compiler 30
GPS 89
pige 6, 30, 303
raccourci clavier 389
zoomer 85
Singleton 112, 167
sizeToFit 103
SKPayment 174
SKPaymentTransactionObserver 174, 175
SKProductsRequestDelegate 173
Slider 41
SMS 264
Socket 152
Span 93

403

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

404

Index

SQLite 310
delete 321
insert 310
sqlite3 316
Store 164
StoreKit 167
superview 198
Synchrone
GET 248
POST 249
@synchronized 113
@synthesize 64, 190, 298, 300, 357

T
tab bar 21, 212
Tche de fond 116
temps additionnel 118
Taille, paquets 128
Taps conscutifs 160
Texte
centrer 28
champ
ajouter 55
e-mail 261
rtracter 56
localiser 287
scuris 40
TextField 40
Text view 39
Thread
asynchrone 246
atomic 25, 300
bassin dautorelease 43, 301, 328
GCD 117
multithreading 25, 113
nonatomic 25, 300
parser 328
principal 42, 246
UIKit 42

01_BlocN_iPhone.indd 404

Timer 219
Token 152
Traduire
application 287
langues 291
Transition
animer 199
Objective-C 7
vue 21

U
UIAccelerometer 112
UIActionSheet 257
UIActivityIndicatorView 42
UIButton 40, 50, 244
UIDatePicker 80, 121
UIKit 22, 33, 42
UIImagePickerController 157
UIImageView 157
UILabel 244
UIModalTransition 204
UINavigationController 311
UIPickerView 78
UIPickerViewDelegate 78
UIPopoverController 374
UISegmentedControl 209, 244
UISplitViewController 374
UITabBar 217
UITabBarController 212
UITabBarDelegate 216
UITabBarItem 217
UITableViewCell 312
UITableViewCellStyle 72, 75
UITableViewController 70, 181, 310,
332, 337, 379
UIView 161, 189
UIViewAnimationCurve 230
UIViewAnimations 225
UIViewController 189
UIWindow 34
URL 246

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

V
Vido 140
afficher 141
boucle 145
contrleurs 141
cran 145
plein 146
local 143
pause 144
play 144
streaming 140
taille 145
web 143
viewWillAppear 199
viewWillDisappear 199
Vue
ajouter 144, 194
animation 201
contrleurs 215
dplacer 219
changer 192
gestion 189, 191
hirarchie 201
insrer 194
modale 390
navigation 201
pile 190

Index

01_BlocN_iPhone.indd 405

X
Xcode 21
XML 323

Z
Zombies 309
Zoom
animation 230
simulateur 85
valeur 93

405

18/08/10 12:50

Proprit
de Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 2011

PROGRAMMEZ
PR
ROG
OGRA
RAMM
MMEZ
MM
E P
EZ
POU
POUR
OUR
R

iPhone, iPod touch, iPad


avec
av
ec iOS
iOS
S4
Le compagnon indispensable pour sinitier
au dveloppement iPhone/iPad !
37 fiches thmatiques avec des exemples
concrets de ralisation dapplications, pour
apprhender les diffrentes fonctionnalits
ncessaires llaboration dapplications
performantes, varies et ergonomiques pour les
utilisateurs du monde entier.
Que vous soyez simple dbutant ou dj sensibilis au
dveloppement dapplications iPhone/iPad, ce guide pratique,
constitu de tutoriels thmatiques, vous accompagnera tout au
long de votre apprentissage.

Niveau : Dbutant / Intermdiaire


Catgorie : Dveloppement mobile

Dcouvrez les outils ncessaires pour dmarrer et concevoir une


premire application lmentaire
Manipulez des lments simples dinterface et explorez le kit de
dveloppement iPhone (UIKit)
Tirez parti des fonctionnalits natives comme laccs liPod, le
bluetooth, lacclromtre, le push
Crez des animations, communiquez avec un site web, insrez de
la publicit
Traduisez vos applications, grez la mmoire, sauvegardez des
donnes
Retrouvez tous les codes sources de louvrage et partagez votre
exprience avec dautres dveloppeurs sur un espace ddi
lchange autour de chaque projet de ce livre sur le forum
communautaire de iPuP : http://www.ipup.fr/forum.

Pearson Education France


47 bis rue des Vinaigriers
75010 Paris
Tl. : 01 72 74 90 00
Fax : 01 42 05 22 17
www.pearson.fr

iPuP

La mthode
iPuP

Aprs avoir crit des tutoriels


et aid des centaines de
membres dbuter, lquipe
iPuP vous propose avec
ce livre de dcouvrir ou
redcouvrir le dveloppement
iPhone/iPad travers
une mthode qui a fait
ses preuves auprs de la
communaut francophone.

ISBN : 978-2-7440-4168-6

Proprit
Albiri Sigue <tag.tog@gmail.com>
customer 27921 at Fri Mar 11 19:30:39
+0100 de
2011

Vous aimerez peut-être aussi