All posts by yunes

TP iOS «gallerie»

En partant du template pour iOS Master/Detail, écrire une application permettant d’exploiter les données du fichier XML : images.xml.

Le fichier XML devra être chargé dynamiquement par l’application (il ne devra pas être contenu dans le déploiement).

La vue principale devra faire apparaître le nom des rubriques (niveau <category>) et dans chaque catégorie les items via leur titre (<title>) et leur icône (<thumbnail>) s’il elle existe.

En sélectionnant un item l’application ouvrira une vue de détail faisant apparaître :

  • le titre (<title>),
  • l’image grand format (dont l’url est donnée dans <image>) et,
  • un éventuel lien qui permettra d’accéder à la page web indiquée (si le lien existe sous le nom <weblink>).

Indications : le parsing du XML s’effectuera à l’aide de NSXMLParser, qui est un parser SAX.

  • commencer par créer un type de données pour représenter les items,
  • créer «en dur dans le code» quelques items avec les bonnes valeurs,
  • faire en sorte que le Master-Detail fonctionne avec ces données (on peut oublier la gestion de l’icône),
  • ensuite rajouter la fonctionnalité, dans la vue de détail, permettant en cliquant d’aller sur le la page web indiquée,
  • ensuite rajouter le parsing du XML,
  • ensuite rajouter la présentation de l’icône dans la vue maître.

Voilà à quoi cela pourrait ressembler :

TP MacOS «StarWars»

Écrire à l’aide de SpriteKit un jeu dans lequel :

  • le joueur se déplace verticalement mais sur une verticale située à gauche de l’écran
  • les aliens apparaissent au fur et à mesure par intervalles tirés au hasard, à droite à une position tirée au hasard et avec une vitesse variable selon les cas…
  • si le joueur touche un alien, son score est incrémenté de 1

On pourra y ajouter ce qu’il plaira selon les envies… Par exemple d’autres «aliens» qu’il faut éviter sous peine de mort immédiate, d’autres «aliens» qui lancent des balles qui peuvent détruire le joueur…

Pour vous aider :

  • l’appel à intervalles réguliers d’une méthode sur un objet peut-être obtenu à l’aide de la classe Timer (du framework Foundation) et en particulier de la factory init(timeInterval: TimerInterval, target: Any, selector: Selector, userInfo: Any?, repeats: Bool)

TTTFS — The Tiny Toy File System

Projet de programmation C et programmation Systèmes

Bas niveau

Cette couche logicielle a pour but de simuler la couche d’accès de bas niveau à un dispositif de stockage permanent et de masse (type «disque dur»).

Un «disque dur» sera représenté par un fichier ordinaire sur le système hôte. Le nom de ce fichier sera par défaut disk.tfs, mais pourra être tout autre nom approprié que l’utilisateur pourrait souhaiter. Pour cette couche, le contenu du fichier sera vu comme une collection ordonnée de N blocs contenant chacun 1024 octets. La taille N sera déterminée à la construction du disque. Aucune information particulière ne doit être inscrite dans les blocs du disque dur (peut importe), sauf dans le premier bloc (bloc de numéro 0).

Il est demandé de produire une bibliothèque de fonctions permettant de manipuler ce disque. Certaines de ces fonctions seront considérées comme des fonctions internes (non visibles des couches supérieures) :

  • error read_physical_block(disk_id id,block b,uint32_t num);, qui lit depuis le disque d’identité donnée (voir plus bas), le bloc de numéro indiqué;
  • error write_physical_block(disk_id id,block b,uint32_t num);, qui écrit sur le disque le bloc de numéro donné.

Les autres fonctions seront considérées comme faisant partie de l’API de manipulation du disque (dont les prototypes devront nécessairement aparaître par inclusion du fichier ll.h) :

  • error start_disk(char *name,disk_id *id); qui permet de manipuler un disque en lui associant une identité dynamique;
  • error read_block(disk_id id,block b,uint32_t num); qui permet de lire un bloc sur le disque (lire annexe sur la raison de différencier cette fonction et la fonction read_physical_block);
  • error write_block(disk_id id,block b,uint32_t num); qui permet d’écrire un bloc sur le disque (même remarque que la fonction précédente);
  • error sync_disk(disk_id id); (voir annexe – en première approximation cette fonction peut ne rien faire du tout;
  • error stop_disk(disk_id id) qui permet de terminer une session de travail sur un disque.

À l’aide de cette API, il est demandé d’écrire la commande tfs_create -s size [name] permettant de créer un fichier qui contiendra les données du disque de nom name (par défault disk.tfs) et où size est le nombre de blocs qui constitueront le disque. Cette commande aura pour effet de créer un fichier sur le système hôte de la taille adéquate et contenant les informations suivantes sur le premier bloc :

  • avant toute chose les nombres sont toujours sur 32 bits et stockés sur le disque en little-endian et toujours alignés sur 4-octets;
  • le premier nombre du bloc 0 sera le nombre total de blocs du disque.

À l’aide de cette API, il est démandé d’écrire la commande tfs_partition -p size [-p size]... [name] permettant de partitionner l’espace physique du disque en paquets de blocs. Cette partition consiste à :

  • écrire le nombre de partition en seconde position du bloc 0 (seconde position signifie à partir de l’octet 4…)
  • écrire pour chaque partition sa taille, dans l’ordre et à la suite.

À l’aide de cette API, il est demandé d’écrire une commande tfs_analyze [name] qui permet d’obtenir les informations d’un disque : sa taille, son nombre de partitions, la taille de chaque partition, etc.

La bibliothèque devra être accompagnée et les commandes devraont être accompagnés des fichiers d’entêtes adéquats ainsi que d’un petit manuel.

Un makefile devra être produit permettant de nettoyer l’arborescence (make clean) et de créer la bibliothèque dynamique (libll.so) ainsi que les exécutables de différentes commandes.

Un exemple de disque physique : 

TTTFS Volume

Attention: tout ce qui est désigné comme nombre entier dans la suite et qui est destiné à être stocké sur le périphérique doit être codé sur 32 bits en little-endian et calés sur 4 octets.

Le système de fichier est construit par dessus le disque physique par construction d’algorithmes qui manipulent des données structurées accessibles depuis/vers ce disque. On notera que la seule communication de base possible avec le disque est la lecture ou l’écriture de bloc (voir API bas niveau).

Attention: un volume doit être auto-contenu, ce qui signifie que tous les références désignent des choses à l’intérieur du volume lui-même. Par exemple, lorsqu’on parle d’un numéro de bloc, il s’agit d’un numéro de bloc de la partition (pas d’un numéro de bloc physique).

Un système de Fichier TTTFS ou volume TTTFS occupe une partition entière d’un disque dur et impose sa propre structure sur l’usage des blocs :

  1. le bloc de description (premier bloc de la partition) — TTTFS Description Block
  2. la table des fichiers (quelques blocs suivants) — TTTFS File Table
  3. les blocs de données (le reste) – TTTFS Data Blocks

Un volume TTTFS contient des structures logiques :

  • la liste des fichiers libres (voir plus loin TTTFS File Table),
  • la liste des blocs libres (voir plus loin TTTFS Free Blocks Chain).

TTTFS Description Block

Le bloc de description d’un volume TTTFS est constitué de nombres (32 bits écrits en little-endian et toujours sur des frontières de 4-octets). Il contient les informations suivantes consécutivement et dans l’ordre :

  • L’identification de la version TTTFS, le TTTFS_MAGIC_NUMBER qui est égal à 0x31534654,
  • La taille d’un block TTTFS mesuré en octets, TTTFS_VOLUME_BLOCK_SIZE, pour la version 1 de TTTFS c’est égal à 1024,
  • Le nombre total de blocs du volume, TTTFS_VOLUME_BLOCK_COUNT, pour TTFS version c’est la taille de la partition,
  • Le nombre actuel de blocs libres du volume, TTTFS_VOLUME_FREE_BLOCK_COUNT,
  • Le numéro du premier block libre du volume, TTTFS_VOLUME_FIRST_FREE_BLOCK,
  • Le nombre total de fichiers supportables par ce volume, TTTFS_VOLUME_MAX_FILE_COUNT,
  • Le nombre de fichiers actuellement libres dans ce volume, TTTFS_VOLUME_FREE_FILE_COUNT,
  • Le numéro du premier fichier libre du volume, TTTFS_VOLUME_FIRST_FREE_FILE,

Certaines de ces valeurs sont à déterminer en fonction des paramètres utilisés par le formatage TTFS (voir plus loin).

Ce bloc est toujours considéré comme occupé, il ne peut être utilisé pour autre chose.

Attention, tous les nombres sont représentés en little-endian et sur 32 bits et calés sur des frontières de 4 octets…

TTTFS File Table

Le table des fichiers d’un volume TTFS est constitué d’une table d’entrée de fichiers contenus dans un ensemble de blocs consécutifs du volume et situés à la suite du bloc de description. Le nombre d’entrée de cette table est contenu dans le bloc de description (TTTFS_VOLUME_MAX_FILE_COUNT). Les blocs qui contiennent cette table doivent toujours être considérés comme occupés, il ne peuvent servir à autre chose.

Une entrée de la table des fichiers est structurée de la manière suivante :

  • La taille du fichier, tfs_size, exprimée en octets
  • Le type du fichier, tfs_type, qui peut prendre les valeurs suivantes : TFS_REGULAR=0, TFS_DIRECTORY=1, TFS_PSEUDO=2
  • Le sous-type du fichier, tfs_subtype qui n’est utilisé que pour le type TFS_PSEUDO et qui dans la version 1 peut prendre les valeurs TFS_DATE=0, TFS_DISK=1
  • 10 numéros de blocs, le tableau tfs_direct, qui peuvent être utilisés pour retrouver des données du fichier.
  • 1 numéro de bloc, tfs_indirect1, qui contiendra des numéros de blocs qui contiennent des données du fichier.
  • 1 numéro de bloc, tfs_indirect2, qui contiendra des numéros de lbocs qui contiennent des numéros de blocs qui contiennent des données du fichier.
  • 1 numéro de fichier, tfs_next_free, utilisé pour le chaînage libre.

Il existe deux logiquement deux types d’entrées :

  • les entrées occupées, pour lesquelles tfs_next_free ne correspond à rien et les autres informations doivent être telles qu’elles puissent permettre de manipuler le contenu d’un fichier. Les numéros de bloc, directs ou indirects désignent les blocs de données du fichier.
  • les entrées libres, dont le nombre est contenu dans le bloc de description (TTTFS_VOLUME_FREE_FILE_COUNT) et la première d’entre elles à pour numéro celui contenu dans le bloc de description (TTTFS_VOLUME_FIRST_FREE_FILE). Les entrées-libres sont chaînées les unes aux autres, en utilisant le champ tfs_next_free. La valeur du champ correspondant pour l’une au numéro de l’entrée libre qui la suit logiquement. La dernière entrée de la liste aura pour suivante son propre numéro. Ainsi si la liste des entrées libres est (dans l’ordre) 13, 38, 47, 8. Le chaînage sera next(13)=38, next(38)=47, next(47)=8 et next(8)=8. TTTFS_VOLUME_FIRST_FREE_FILE devra être égal à 0 s’il n’y a plus d’entrées libres.

TTTFS Data blocks & Free Blocks Chain

Les blocs de cette zone du volume contiennent :

  • des blocs occupés par des données de fichiers, les seuls fichiers qui possèdent des données sont les fichiers orinaires (type TFS_REGULAR) et les répertoires (type TFS_DIRECTORY) (voir plus loin).
  • des blocs occupés par des blocs d’indirection, ceux-ci contiennent des numéros de blocs de données ou des blocs d’indirection de niveau supérieur.
  • des blocs libres. Ceux-ci sont chaînés les uns aux autres à la manière des entrées libres de la table des fichiers. Le premier de cette liste a pour numéro celui indiqué par le champ TTTFS_VOLUME_FIRST_FREE_BLOCK du bloc de description du volume. Ensuite les blocs sont chaînés les uns aux autres. Le numéro du bloc libre suivant est enregistré comme le dernier entier du boc précédent. Le dernier a pour suivant lui-même.
TFS Directories

Un répertoire est un fichier contenant des entrées de répertoire. Chaque entrée est constituée de deux données :

  • un numéro de fichier
  • une suite de 28 caractères constituant une chaîne de caractère à la C c’est-à-dire nécessairement terminée par ASCII-0.

Un répertoire dit vide contient deux entrées : l’une de nom “.” et désignant lui-même et l’autre nommée “..” désignant son répertoire parent. La racine du système de fichier est le fichier de numéro 0, ne peut être supprimé et est son propre parent. Pour des raisons pratiques, il est autorisé d’avoir des entrées de répertoire vides, c’est à dire ne correspondant à rien, dont le nom est la chaîne vide (réduite au seul caractère nul).

Un exemple de partition : 

TFS Special Files

Les fichiers de type pseudo, sont particulier puisqu’il n’ont pas vraiment de contenu. Leur contenu est virtuel, c’est-à-dire dynamiquement déterminé.

Pour le type TFS_DATE, un accès à ce fichier permet d’obtenir la date courante exprimée sous la forme d’un entier exprimant le nombre de secondes écoulées depuis le démarrage du disque.

Pour le type TFS_DISK, les accès correspondent aux données brutes du volume courant.

TFS Pathnames

Les chemins manipulables par l’API TFS ont tous la forme :

FILE://disk/volume/rep1/rep2/name où :

  • disk désigne le disque contenant un ou plusieurs volumes TFS. Lorsque disk est égal à “HOST”, les accès sont réalisés directement sur le système de fichiers hôte (l’Unix sous-jacent).
  • volume désigne le volume TFS sur le disque (0, 1, etc.)
  • la suite constitue le chemin pour se déplacer depuis la racine vers le fichier concerné, les “/” sont des séparateurs.

TODO

Il est demandé d’implémenter une commande tfs_format -p partition -mf file_count [disk] permettant de créer un système de fichier minimal sur la partition donnée du disk donné; système de fichiers ayant pour nombre maximal de fichiers le file_count` donné et contenant un répertoire racine vide.

Il est demandé d’implémenter une bibliothèque de fonctions diverses et variées utiles permettant de (liste non-exhaustive) réaliser des opérations de bas-niveau sur un volume donné :

  • mettre un bloc dans la liste des blocs libres
  • supprimer un bloc de la liste des blocs libres
  • mettre une entrée de répertoire dans la liste des entrées libres
  • supprimer une entrée des entrées libres
  • ajouter un bloc dans la liste des blocs d’un fichier
  • supprimer un bloc dans la liste des blocs d’un fichier
  • découpage d’un chemin par itération
  • libérer les blocs de données d’un fichier ou répertoire
  • etc.

Ces fonctions devraient permettre de créer une API permettant de manipuler le système de fichier de façon standard :

  • int tfs_mkdir(const char *path, mode_t mode);
  • int tfs_rmdir(const char *path);
  • int tfs_rename(const char *old, const char *new);
  • int tfs_open(const char *name,int oflag, ...);
  • ssize_t tfs_read(int fildes,void *buf,size_t nbytes);
  • ssize_t tfs_write(int fildes,void *buf,size_t nbytes);
  • int tfs_close(fildes);
  • off_t tfs_lseek(int fildes,off_t offset,int whence);
  • DIR *opendir(const char *filename);
  • struct dirent *readdir(DIR *dirp);
  • void rewinddir(DIR *dirp);
  • int closedir(DIR *dirp);

Ces fonctions devront se comporter comme les appels systèmes Unix usuels (même valeurs possibles, même comportement, etc). La différence majeure notable étant l’interprétation du paramètre name de la fonction tfs_open qui devra supporter le schéma de nommage proposé plus haut.

Ces fonctions devront être les seules fonctions visibles de la bibliothèque dynamique libtfs.so qui servira à obtenir un exécutable supportant le système de fichiers TFS.

Il est aussi demandé d’écrire des commandes comme (liste non-exhaustive):

  • tfs_cp
  • tfs_mv
  • tfs_rm
  • tfs_cat
  • tfs_mkdir

Pour copier un fichier du monde Unix au monde TFS on pourra utiliser une commande comme tfs_cp FILE://HOST/home/yunes/truc.txt FILE://disk.tfs/0/dir/toto.txt qui permet donc de copier le contenu du fichier /home/yunes/truc.txt du système de fichier Unix vers le fichier de référence /dir/toto.txt situé sur le volume numéro 0 du disque tfs de nom disk.tfs.

Il est demandé de décrire soigneusement l’ensemble des fonctions réalisées, permettre la compilation via l’outil make, utiliser git comme système de gestion de version (on rappelle que la machine moule.informatique.univ-paris-diderot.fr:8080 héberge un gitlab utilisable…).

Les documentations doivent être fournies dans un format raisonnable, donc autre que du simple texte, par exemple HTML (un regard vers l’outil Doxygen ne sera pas inutile).

Annexe

Système de cache…

Foody

Il est demandé d’utiliser SpriteKit pour réaliser un jeu. La vidéo suivante en serait une version primitive :

Le scénario est qu’une petite bestiole se déplace dans un labyrinthe (celui-ci n’est pas illustré dans la vidéo) et doit aller chercher de quoi manger (ceci augmente son score comme dans la vidéo) mais qu’en chemin il y a des pièges de différentes sortes. Lorsque le joueur n’a plus de points de vie, la partie est finie.

Les pièges peuvent être :

  • des trappes qui le font passer dans un autre labyrinthe (pour aller chercher d’autres choses à manger). Les trappes se rendent visibles pour un temps assez court mais sont normalement invisibles. Elles peuvent aussi changer de place au cours du temps
  • des pièges qui amputent la vie du joueur (lorsque la vie s’épuise la partie est terminée)

La nourriture peut être de différentes sortes :

  • de la nourriture simple qui ne fait qu’augmenter le score
  • de la nourriture magique qui fait apparaître les trappes
  • de la nourriture régénérante qui permet de récupérer quelques points de vie

On prendra soin d’essayer d’animer si possible agréablement le jeu par différents effets :

  • sonores
  • graphiques

VisuImage

Première partie : une prise en main (pas de code à écrire, enfin!?)

  1. Lancer xcode (qui se trouve normalement dans le répertoire /Applications) et créer une application de type Cocoa Application (langage Swift de préférence, sans StoryBoard, ni tests unitaires, ni Core Data). Compiler et tester le modèle par défaut afin de vérifier que l’environnement de développement est correctement installé et configuré.
  2. Explorer le projet afin d’en anaylser la structure (fichiers sources, ressources, frameworks, etc).
  3. Revenir à la sélection du premier élément représentant le projet et observer les différentes options de configuration du projet et de la cible (métadonnées, options de compilation, etc). Attention à ne rien changer que vous ne sauriez remettre en l’état…
  4. Sélectionner le fichier MainMenu.xib et éditer l’interface de sorte que lui soit rajoutée un objet permettant de visualiser une image (piste : NSImageView).
  5. Configurer ce visualiseur d’image de sorte qu’une image de votre choix y soit présente au lancement de l’application. Attention, cela nécessitera sans doute d’ajouter cette image dans le projet lui-même… Tester.
  6. Modifier les caractéristiques de cet objet visualiseur d’image de sorte qu’il soit autorisé de déposer une image quelconque depuis l’extérieur de l’application (piste : propriété Editable). Tester.

Seconde partie : un peu de code… (ah… Enfin!)

  1. Créer un nouveau projet vierge de type Cocoa Application (langage Swift de préférence). Le configurer.
  2. Y ajouter une nouvelle classe à l’aide du menu File > New > File.... Observer l’instanciation d’un modèle de source de classe Swift.
  3. Ajouter, dans la classe une action de nom click:. La signature devra en être @IBAction func click(sender: AnyObject?). Faire en sorte qu’un message soit affiché (via Swift.print) lorsque cette méthode est appelée.
  4. Éditer MainMenu.xib afin d’ajouter à l’interface un simple bouton.
  5. Dans la partie Objects de l’interface, rajouter une instance d’objet (NSObject). Modifier la classe de cet objet en choisissant la classe précédemment créée.
  6. En tenant le bouton droit de la souris, tirer un trait du bouton vers l’objet instancié. Dans le menu contextuel qui apparaît choisir l’action adéquate… Observer le lien créé via l’inspecteur du bouton et/ou de l’objet : onglet Connections inspector en partie droite de la fenêtre principale.
  7. Compiler et tester votre première application Mac.

Troisième partie : le monde est vaste… (internationalisation)

  1. Reprendre l’application précédente et modifier le projet de sorte qu’il soit localisable (aller sur le projet et modifier sa configuration générale pour obtenir des localisations)
  2. Ajouter un fichier de ressources de type Strings File de nom Localizable.
  3. Revenir sur le fichier de localisation créé précédemment et le rendre localisable (via l’inspecteur) en choisissant une langue parmi celles offertes.
  4. Une entrée d’un tel fichier est toujour au format "clé" = "valeur";. Modifier les fichiers de localisation de sorte qu’à la clé "clic" soit associé un texte particulier pour chaque langue.
  5. Modifier le source de la classe de sorte que Swift.print affiche un message localisée selon l’environnement. Pour cela, on peut utiliser la méthode NSLocalizedString (consulter la documentation pour en connaître les détails).
  6. Compiler et tester que le message s’affiche correctement.
  7. Modifier la langue de l’environnement (via les Préférences Systèmes…) et relancer l’application pour constater que la localisation fonctionne.

Quatrième partie : une image vaut mieux que cent discours…

Si ce n’est pas déjà récupéré depuis la première question:

  1. Ajouter dans le projet une image
  2. Dans l’interface, ajouter une ImageView
  3. Modifier les propriétés de l’Image View pour qu’elle affiche l’image contenue dans le projet.
  4. Compiler, tester.

Cinquième partie : qu’est-ce qu’il y a au menu aujourd’hui ?

  1. Vérifier qu’à l’exécution l’option Open du menu est désactivée.
  2. Observer les liaisons du FirstResponder et de l’option de menu Open
  3. Créer une sous-classe Swift de NSImageView
  4. Modifier la classe de l’instance précédente de l’Image View de votre interface
  5. Ajouter à cette classe une méthode de signature @IBAction func openDocument:(sender: AnyObject?) qui affiche un message simple sur la console (Swift.print you know?)
  6. Faire une liaison de l’option de menu vers l’instance de la classe
  7. Compiler et vérifier que l’option du menu est active!
  8. Implanter de quoi sélectionner un fichier image (NSOpenPanel) et qu’il s’affiche dans l’Image View
  9. Compiler, tester

Sixième partie :

  1. Livrer le code à l’enseignant. Code expurgé de tout exécutable, etc. Vérifier que votre livraison est correcte (par exemple en testant sur une autre machine et avec le compte d’un camarade, celui-ci devrait pouvoir compiler et tester sans erreur aucune!). Pour les détails de la livraison, s’adresser à l’enseignant.

SuperDestructor

On souhaite réaliser un jeu à l’aide du framework SpriteKit.

Le principe général du jeu

Le jeu se situe dans l’espace que tout le monde sait être hostile. Le joueur doit pouvoir contrôler à l’aide des flèches de clavier le déplacement d’un vaisseau spatial. Ce vaisseau spatial est équipé d’un système de défense très perfectionné permettant d’envoyer des missiles vers l’avant.

L’hostilité rencontrée par le vaisseau durant ce jeu est constituée d’un autre vaisseau mais ennemi. Celui-ci se déplace aléatoirement dans l’espace et a pour activité principale de lancer des obus vers le vaisseau du joueur.

Description précise

Le vaisseau du joueur peut se déplacer dans les 4 directions : gauche, droit, haut, bas à l’aide des flèches du clavier. Mais son déplacement est limité à une partie précise de l’espace, uniquement la moitié gauche de la fenêtre de jeu; impossible de sortir de cette zone. Le lancement de missile s’effectue à l’aide de la touche d’espacement. La fréquence de tir est limitée, mais cette limitation doit s’alléger au cours du temps afin d’améliorer les possibilités de défense du joueur. La vitesse du missile peut aussi être variable au cours du temps.

Le vaisseau ennemi se déplace aléatoirement (mais pas trop tout de même) sur une ligne verticale située à droite de l’écran, mais il ne sort jamais de l’écran. À intervalles réguliers il lance un obus à l’horizontale de sa position. La vitesse de déplacement du vaisseau ennemi, la fréquence de tir et la vitesse des missiles doivent augmenter au cours du jeu pour augmenter la difficulté de jeu.

Le joueur gagne un point lorsqu’il arrive à toucher le vaisseau spatial ennemi mais perd 10% de sa force vitale à chaque fois qu’un obus l’atteind. Le joueur possède deux vies au départ. Lorsqu’il n’a plus de vie le joueur a perdu et son score doit être enregistré.

Réalisation

Il est impératif d’employer SpriteKit pour gérer l’ensemble des aspects du jeu.

Il est possible de modifier légèrement le jeu mais il est impératif d’en respecter l’esprit.

Il est possible de rajouter des difficultés, des décors, des animations, des effets sonores en tout genre; mais ce n’est pas le but principal! Le jeu prime!

iOS+XML


On souhaite réaliser une application iOS permettant de visualiser des données et de naviguer en leur sein. Ces données décriront les attributs associés aux cours dispensés à l’UFR d’Informatique et à leurs enseignants affectés mais aussi les relations entre ces différentes entités. L’application sera réalisée par étapes successives, au final on devra pouvoir naviguer et obtenir différents effets (envoyer un mail à un enseignant, obtenir sa photo, naviguer sur sa page web, etc, tout cela depuis la page d’un cours ou d’un diplôme. Ce TP débouchera (au final) sur un rendu de la réalisation qui pourra être faite par groupe de 2.

XML

  1. Dans un premier temps il est nécessaire de structurer les données. Il est donc demandé de spécifier un format de données XML (pas besoin de DTD on ne fera pas de validation), balises, attributs permettant de décrire l’ensemble du cursus d’informatique de l’UFR, licence, master, avec pour chaque diplôme les années, L1, L2, L3, M1, M2, pour chaque année la liste des UE/cours (intitulé, horaires, salles, etc), et pour chaque cours les chargés de cours et TD et TP. Pour chaque enseignant sa page web, son mail, sa photo, une présentation audio, vidéo, etc. Il peut (doit?) s’agir de fichier XML distincts (cours.xmlenseignants.xmlhoraires.xml?).
  2. Dans un second temps, il faut créer quelques données utilisables (inutile de décrire vraiment l’ensemble des cours et cursus) en quantité suffisantes.
  3. Déposer ses fichiers sur un serveur web (vous devez normalement avoir accès à votre propre page web de l’UFR en créant un dossier personnel de nom public_html, si ce n’est pas le cas trouvez une solution (avec les enseignants, etc).

iOS

  1. Créez une application iOS de base (choisissez par exemple la version la plus élémentaire), compilez, excutez afin de vérfier que tout fonctionne bien.
  2. Créez des structures de données (des classes?) permettant de représenter facilement dans votre application les données extraites du fichier XML
  3. Modifiez le délégué d’application de sorte que le fichier XML soit chargé et parsé de façon à remplir votre structure de donnée (via NSXMLParser?)
  4. L’interface dont vous aurez besoin ne devra pour l’instant contenir rien d’autre que des informations relatives au chargement :
    • nombre de cours
    • nombre d’enseignants
    • etc.

Présentation de données

Attention le travail suivant est (très) conséquent et nécessitera probablement plusieurs séances… Ce sera l’objet des séances suivantes…

  1. Présentez les données de façon à rendre ergonomique au possible votre application, nous vous suggérons d’utiliser des mélanges subtils de
    • webviews pour présenter des données HTML (page web des enseignants?)
    • tableviews pour présenter des listes longues/variables d’items (cours?)
    • tabbars pour présenter des collections restreintes et fixes de catégories (diplômes?)
    • pickerviews (diplômes/cours/années?)
    • imageviews (photos?)
    • objets du framework AVFoundation pour présenter de l’audio/vidéo

Mix xcode+quartz

On souhaite réaliser un diaporama avec effets graphiques. L’effet de base est illustré dans la vidéo suivante : exemple. La réalisation est en deux parties :

  1. réaliser une composition Quartz permettant d’animer l’affichage d’un ensemble d’images
  2. intégrer et piloter cette composition depuis une application MacOSX

Première partie : Quartz

Revenir sur votre composition Quartz d’animation (si vous n’en avez pas une fonctionelle, fabriquez en une simple).

  1. Modifiez votre composition de sorte à exporter certains paramètres comme des paramètres d’entrée, c’est-à-dire des paramètres dont les valeurs pourront être injectées depuis l’extérieur de la composition. Commencer par le paramètre contrôlant la vitesse de rotation. Sélectionner par clic doirt l’entrée du patch Quartz et choisir Publish Inputs. Choisir un nom adéquat pour l’export.
  2. Tester que ce paramètre est effectivement utilisable en sélectionnant l’onglet Parameters dans la barre d’outils. Modifiez la valeur directement dans le formulaire présenté et vérifier que cela fonctionne comme attendu.

Seconde partie : XCode

  1. Créer ou modifier une application xcode intégrant la composition Quartz précédente. Ajouter deux boutons, l’un de libellé +vite l’autre -vite.
  2. Ajouter une classe Cocoa descendante de NSObject contenant un attribut entier correspondant à la valeur vitesse et un Outlet de type QCView.
  3. Instancier via le constructeur d’interface un objet de la classe précédente. Établir le lien Outlet entre l’objet et la QCView.
  4. Établir un lien réactionnel (une Action) entre les boutons +vite et -vite et la classe de l’objet métier. Une action s’appellera plusVite et l’autre moinsVite.
  5. Modifier le code de ces méthodes de sorte à modifier en conséquence la valeur de l’attribut stocké par l’objet (afficher cette valeur via un appel à NSLog. Tester. Question : comment est initialisé l’attribut ? Est-ce la bonne valeur de départ ?
  6. Rechercher dans la document de la QCView comment modifier la valeur d’un paramètre exporté d’une composition Quartz jouée dans une QCView. Faire en sorte que les deux actions modifient le paramètre. Tester.

Troisième partie : XCode

  1. Rechercher dans la documentation comment une QCView peut en retour appeler du code contenu dans l’application (piste: cet appel est effectué sur une base temporelle). Implanter du code de sorte qu’une méthode de l’objet métier soit appelée sur une base temporelle (y placer un appel à NSLog).
  2. Rechercher dans la documentation comment les valeurs de paramètres de sortie de composition Quartz peuvent être consultées dans un code applicatif. Modifier la composition Quartz pour produire une valeur indiquant qu’il est temps de changer d’image. Rajouter une entrée publique à la composition permettant d’injeter une image NSImage.
  3. Modifier le code de sorte que lorsque cet indicateur est positionné, l’application change d’image tournante (on s’autorisera à basculer entre deux images pour commencer). Consulter la documentation de NSImage pour retrouver comment des images peuvent être lues depuis le système de fichiers.
  4. Modifier le code de sorte à ajouter un NSOpenPanel lorsqu’on choisit l’option Ouvrir du menu de l’application. Ce Panel devra permettre de choisir dans le système de fichiers un répertoire contenant des images (on pourra supposer que le répertoire contient effectivement des images et uniquement des images). Les images du répertoire seront utilisées dans l’ordre comme suite d’image à afficher par la composition animée.

Quatrième partie : cosmétique + extension…

  • Fournir une application au standard attendu : icône, etc
  • Définir une extension de fichier .dpr (.diaporama) reconnue par l’application dont le contenu pourra être une liste d’URL désignant des images qui seront utilisées lors de la projection

XCode (découverte)

Après avoir réalisé une composition Quartz dans le TP1, on souhaite l’intégrer à une application MacOSX.

Les objectifs de ce TP :

  • Découvrir XCode
  • Ajouter des composants graphiques
  • Créer des actions et les lier aux composants
  • Intégrer une composition Quartz et la déclencher par un bouton

La liaison des paramètres de la composition avec des variables de l’application se fera dans un prochain TP (suspense…)

Partie préliminaire : les supports de cours

Pour ce TP, on utilisera Objective-C et le framework Foundation.

  1. Télécharger les deux supports de cours pdf et les enregistrer
  2. Ouvrir chacun de ces pdf et les mettre en affichage « Planche contact » (Alt-Cmd-4) puis grossir au maximum le zoom (Cmd-+).
  3. Conserver ces documents ouverts et y chercher des informations ou des exemples chaque fois que nécessaire.

Première partie : prise en main

  1. Lancer xcode (qui se trouve normalement dans le répertoire /Applications) et créer une application de type Cocoa Application (attention à sélectionner le template dans la rubrique OS X et non la rubrique iOS).
  2. Compiler et tester l’application (vide) afin de vérifier que l’environnement de développement est correctement installé et configuré. Une fois lancée (avec Cmd-R), l’application se comporte comme n’importe quelle application MacOSX : lorsqu’elle est sélectionnée, son menu s’affiche tout en haut de l’écran. Pour la retrouver lorsqu’on a sélectionné une autre application active, on peut utiliser Cmd-Tab, et pour la quitter, on utilise son menu ou Cmd-Q.
  3. De retour dans XCode, sélectionner le fichier MainMenu.xib. La feuille quadrillée qui s’affiche est destinée à contenir les fenêtres de l’application. C’est l’éditeur d’interface. Le volet vertical situé entre cette feuille quadrillée et le volet de navigation permet de voir les objets que l’on manipulera. Si ce n’est pas fait par défaut, développer ce volet, soit en cliquant sur l’icône en bas à gauche de la feuille quadrillée, soit par le menu Editor > Show Document Outline.
  4. Dans le volet Document outline, sélectionner l’objet représentant la fenêtre, et si ce n’est pas fait par défaut, déployer son contenu. La fenêtre contient une vue. Toujours dans le volet Document outline, sélectionner la vue, puis à nouveau la fenêtre qui la contient, et observer la différence d’affichage sur la fenêtre de l’application (sur la feuille quadrillée).
  5. S’il ne l’est pas par défaut, afficher le volet des utilitaires, soit par Cmd-Alt-0 (idem pour le cacher), soit par le menu View > Utilities > Show Utilities. Notez à ce propos, que si par suite d’une mauvaise manipulation, vous perdez le volet de navigation (celui de gauche), vous pouvez le retrouver par Cmd-0 ou par View > Navigators > Show Navigator
  6. Sélectionner la vue, puis chercher, dans la bibliothèque d’objets (panneau en bas à droite), un objet de type Label, et le glisser sur la fenêtre de l’application. Remarquer les modifications sur le volet Document outline : deux objets encapsulés ont été ajoutée. Observer les différences d’affichage lorsqu’on sélectionne successivement ces deux nouveaux objets (remarquer en particulier la barre d’adresse, tout en haut de l’éditeur, affichant le chemin complet vers l’objet sélectionné. Chaque noeud de ce chemin est cliquable…)
  7. Modifier le texte de la cellule de texte, enregistrer, compiler puis exécuter l’application.
  8. Ajouter un bouton à la fenêtre de l’application, enregistrer, compiler, tester.
  9. Modifier diverses propriétés d’affichage des objets ajoutés, au moyen des inspecteurs de taille et d’attributs, dans le volet des inspecteurs, en haut à droite.
  10. L’éditeur d’interface comporte également le menu de l’application. Déplacer le menu Format juste avant le menu Help, supprimer le menu Window, enregister, compiler, tester.
  11. Observer dans le Document Outline le type des objets constituant le menu, et ajouter un élément dans le menu View. Renommer cet élément « Message in Console ». Enregistrer, compiler, tester.

Deuxième partie : un peu de code (ou pas)

  1. Ajouter une nouvelle classe au projet à l’aide du menu File > New… (Cmd-N), en sélectionnant une Objective-C class, et en prenant bien soin de la définir commoe sous-classe de NSObject. Deux fichier, .h et .m s’affichent alors dans le navigateur du projet. Laisser pour le moment ces fichiers intacts, mais observer que lorsqu’on édite l’un des deux, il est possible de basculer de l’un à l’autre par les touches Ctrl-Cmd-↑ et ↓.
  2. Pour faire de la place, cacher (momentanément) le volet des utilitaires (Cmd-Alt-0, idem pour le faire réapparaître).
  3. Faire apparaître l’éditeur assistant (Alt-Cmd-Enter ; ou View > Assistant Editor > Show ; ou l’icône du milieu dans le groupe d’icônes Editor tout en haut à droite !)
  4. Dans l’éditeur principal, sélectionner le fichier .m nouvellement créé et dans la barre d’adresse de l’éditeur assistant, si le fichier .h ne s’est pas affiché automatiquement, cliquer sur le premier mot pour sélectionner Counterparts > .h
  5. Sélectionner à nouveau MainMenu.xib, fermer (momentanément) l’assistant éditeur (petite croix tout à droite de sa barre d’adresse) et faire revenir le volet des utilitaires. Ajouter aux objets de l’interface (mais en dehors de la fenêtre de l’application) un NSObject.
  6. Au moyen de l’inspecteur d’identité, modifier le type de l’objet, pour que ce soit la nouvelle classe créée.
  7. Faire à nouveau apparaître l’éditeur assistant (et éventuellement cacher le volet des utilitaires), et sur sa barre d’adresse, cliquer sur le premier mot pour faire apparaître, dans le menu Automatic, le fichier .h de la nouvelle classe. (Il est aussi possible d’y revenir directement avec la flèche qui reprend le fichier précédent, puisque c’est lui qui avait été affiché dans le cas présent).
  8. Dans l’éditeur d’interface, faire apparaître l’élément ajouté précédemment dans le menu View de l’application (Message in Console). Attention, magie, cliquer avec le bouton droit sur ce menu (ou Ctrl-clic si pas de bouton droit) et maintenir appuyé pour tirer un fil bleu depuis le menu vers le contenu du fichier .h dans l’éditeur assistant. Relâcher juste en dessous de la ligne @interface. Dans la boîte qui apparaît, sélectionner Action comme type de connexion, et taper message comme nom d’action, puis valider. La déclaration d’une nouvelle action a dû s’ajouter au code du fichier.
  9. Basculer sur le .m et écrire, comme première ligne de code de ce TP, une instruction qui fait afficher un message quelconque sur la console au moyen de la méthode NSLog (consulter la documentation, et prendre bien garde à la syntaxe des constantes de type NSString, voir le cours Foundation à partir de la page 6).
  10. Enregistrer, compiler, exécuter l’application et tester l’effet du nouveau menu.

Troisième partie : la même chose, à la main

  1. Fermer l’éditeur assistant
  2. Ajouter à la main (!) une action de nom click dans le .h et le .m de la classe. L’effet de cette action sera d’afficher un autre message que précédemment sur la console. Enregistrer.
  3. Dans l’éditeur d’interface, sélectionner le bouton ajouté initialement à la fenêtre de l’application et tirer un fil bleu vers l’instance de la nouvelle classe. Le menu qui s’affiche permet de choisir l’action qui vient d’être créée.
  4. Dans l’inspecteur des connexions, vérifier que la connexion est bien présente. Enregistrer, compiler, tester.
  5. Pour bien prendre le pli, supprimer la connexion dans l’inspecteur, et constater qu’il est là aussi possible de la reformer, en cliquant sur le petit rond pour tirer un fil bleu à nouveau vers l’instance de la classe. De même, la connexion pouvait s’établir de l’instance de la classe via l’inspecteur des connexions de celle-ci, vers le bouton.

Quatrième partie : Quartz is back

  1. Ajouter à la fenêtre de l’application un objet de type QCView.
  2. À l’aide de l’inspecteur des propriétés, éditer l’objet QCView pour qu’il charge la composition Quartz créée au TP précédent, penser à paramétrer cet objet.
  3. Par une des deux méthodes vues précédemment (par l’éditeur assistant ou à la main), ajouter une connexion du QCView vers AppDelegate.h, mais cette fois la connexion sera un outlet (et non une action), de nom renderer, et sa méthode de stockage sera weak.
    (Un Outlet est une propriété visible de l’éditeur d’interface à laquelle on peut associer un objet, c’est une propriété représentant une association au sens UML. Désormais l’objet App Delegate connaît la QCView de l’interface via la propriété renderer). 
    Penser à enregistrer les deux fichiers : l’interface et AppDelegate.h.
  4. Pour que l’objet QCView soit connu du compilateur, il faut ajouter au projet le framework Quartz, et importer Quartz/Quartz.h.
  5. Dans l’interface, associer le bouton au déclenchement de l’action play: de la QCView. Enregistrer, compiler, tester.

Pour aller plus loin

AppDelegateObserver les connexions de l’objet App Delegate et constater que cet objet est connu d’un autre de nom File's Owner via un Outlet à l’intérieur de celui-ci. Observer l’objet File's Owner et constater que sa classe est NSApplication. Utiliser la documentation pour comprendre à quoi ces objets sont utiles.Les projets XCodeDans le navigateur, sélectionner la toute première ligne, qui représente l’intégralité du projet, et observer les différentes options de configuration du projet et de la cible (métadonnées, options de compilation, etc). Attention à ne rien changer que vous ne sauriez remettre en l’état…