Développement sur iPhone

Dr_cube

Membre actif
27 Février 2006
385
28
37
Salut tout le monde !

Je ne sais pas trop où créer ce sujet (forum iPhone ou forum Développement sur Mac), mais je pense que c'est plus adapté ici.

Je débute en programmation sur iPhone (et d'une manière générale en Objective-C et Cocoa), et j'ai besoin des lumières d'un habitué de Cocoa (Touch) pour me sortir d'un problème tout bête. J'imagine que ce topic pourra servir pour d'autres problèmes de ce genre par la suite.

Mon problème est simple :

En partant d'un nouveau projet basé sur des Vues (View-based Application), je cherche à faire une View personnalisée. Et je ne veux pas utiliser Interface Builder. Or ils me disent que la vue est dans un nib file. J'aimerais la programmer à la main.
Donc au départ, dès que le projet est crée, j'ai un AppDelegate, un ViewContoller, et un nib file contenant la vue.
Comment je fais pour créer une vue à la main (et ainsi démarrer mon arborescence de vues), et la faire contrôler par ViewController.
Aussi absurde que ça puisse paraître, je n'ai absolument pas trouvé d'exemple sur le Web pour résoudre mon problème. Tous utilisent Interface Builder pour créer des vues à partir des trucs existants d'Apple. Or pour le moment je n'ai que faire d'une TableView ou des Boutons de l'iPhone. Tout ce que je veux c'est créer des vues qui dessinent des rectangles, des Bezier etc., et qui récupère des événements. Mais comment faire pour que cette vue s'affiche effectivement à l'écran ?

Autre question, est-ce que je peux mettre la partie "contrôle" de mon architecture MVC dans un ViewController ?

Bref, j'ai compris comment écrire des vues etc., mais je n'ai pas compris comment démarrer avec une première vue.
Je rappelle que je souhaiterais tout programmer à la main, sans utiliser IB.

Merci beaucoup pour votre aide !
 
Salut Dr Cube, je pense que tu es sur le bon forum.


Il est normal que tu n'aies pas trouvé d'exemple, car ce que tu veux faire est étrange (du point de vue de Cocoa s'entend). Il est possible de créer une vue en manuel et de la glisser dans la hiérarchie des autres fenêtres et vues, mais c'est compliqué, et c'est totalement inutile dans 99% des cas. Je ne sais pas si tu as l'habitude de Java, mais cette volonté de vouloir tout créer à la mano, ça pue le Swing, qui est à mon sens de la préhistoire. (même sur mon Atari ST, y'avait un éditeur de ressources).

Je ne vais pas tout t'expliquer, mais voici la technique:
• Xcode File > New NSView subclass
• Il faut remplir la méthode drawRect:
Le NSRect passé est la zone à rafraîchir (si tu veux le rectangle contenant la vue, utilise la méthode -bounds])
Pour dessiner une ellipse rouge qui prend toute la vue:
Bloc de code:
oval = [NSBezierPath pathWithOvalInRect:[self bounds]];
[[NSColor redColor] set];
[oval fill];

• Sous IB, tu glisses une Custom View de la palette Library vers ta fenêtre.
• Tu changes sa classe pour qu'elle corresponde à celle de ta nouvelle NSView dans Class Identity.

Et ça marche.

Pour le reste, il va falloir être plus précis (je ne vais pas te piquer ton idée géniale), mais en général, si tu n'arrives pas à faire quelque chose facilement avec Cocoa, c'est que tu t'y prends mal.
J'ai l'impression que tu veux faire des boutons personnalisés; dans ce cas, il faudra hériter des NSCell et voir la doc de NSControl.

P.S.: Je n'ai jamais programmé l'iPhone, mais j'ai cru comprendre que c'était pareil que sur Mac.
 
Salut !

(Roo j'avais écris une réponse assez longue vendredi mais j'ai tout perdu à cause d'une fausse manip...)

Merci beaucoup pour ta réponse ! Ce que je ne savais pas faire, c'était les deux derniers points. Maintenant ça marche parfaitement ! C'est tout simple en fait..

Concernant ce que je dois faire, il ne s'agit pas vraiment d'une idée géniale ^^. Je dois réaliser un menu particulier (circulaire), et donc je ne peux pas utiliser les boutons standards d'Apple. J'ai donc besoin d'une vue personnalisée dans laquelle je peux dessiner mes propres boutons etc. Je ne pense pas qu'il soit possible d'utiliser IB pour créer toutes les vues personnalisées que je vais devoir ajouter dans mon menu. Je pense que je vais ajouter mes vues dans les programmes (à la main), parce que de toute façon leur nombre n'est pas connu à l'avance (le menu est décrit par un fichier XML). Je suis en effet un habitué de Java/Swing ^^. Mais là je pense que je suis obligé d'ajouter mes vues à la main. Sur iPhone c'est assez simple puisqu'il n'y a qu'une seule fenêtre. Il suffit de faire un
[window addSubview:maVue];
(on peut ajouter une vue à une autre vue que window de la même manière).

Tu penses qu'il faut hériter des boutons d'Apple pour créer des boutons personnalisés ? Le problème c'est que je vais avoir presque tout à redéfinir, car sur ces boutons il n'y a pas que le "clic" qui doit être pris en compte. Et il ne sont pas rectangulaires, ce qui demande une logique bien différente des boutons traditionnels. Au départ je pensais que ces boutons seraient des View normales, mais tu me mets le doute...

J'aurais certainement besoin d'utiliser IB lorsque je voudrai faire un menu pour paramètrer mon menu..

Par contre j'ai toujours mon problème avec les ViewController : je n'ai pas vraiment compris de quelle manière je peux mettre la partie "contrôleur" de mon application dans les ViewController, et plus généralement dans des NSController.

Encore merci pour ta réponse qui m'a vraiment aidée.
 
Je suis navré mais je ne vais pas pouvoir t'aider d'avantage, n'ayant jamais personnalisé les contrôles. Ce que tu veux faire n'est pas — à mon sens — une NSView, mais bien un NSControl.
NSControl hérite d'ailleurs de NSView. Rien ne t'empêche de programmer ton menu avec une NSView, mais tu vas te rendre compte que tu vas reprogrammer toutes les méthodes de NSControl. Par contre, je ne sais pas te dire si les différentes zones doivent être rectangulaires et si leur nombre peut être variable.
 
Salut !

Je suis désolé de vous déranger encore une fois, mais ça fait un bon moment que je casse la tête sur un nouveau problème, et je n'arrive plus à avancer...
Je ne pense pas que mon problème soit uniquement dû à l'iPhone, mais bon..

Je dois parser un fichier XML. En lisant les docs d'Apple, j'ai été attiré malgré moi par SAX (NSXMLParser), mais ça ne me convient pas du tout.
J'ai besoin de faire du DOM, et je suis donc tombé sur NSXMLDocument. Jusque là tout va bien, Apple fournit le code d'une méthode (createXMLFromFile: ).
En lisant des messages sur des nombreux forums, j'ai découvert que NSXMLDocument n'est pas implémenté dans l'iPhone, pour des raisons de performances ! Sur certains forums, on lit même que ça marcherait sur le Simulateur, mais pas sur le Device (je ne peux pas tester sur le device en ce moment).

Dans ma version du SDK, NSXMLDocument semble bien reconnu, ça compile sans erreur (alors que sur certains forum, des messages affirment que ça fait des erreurs, mais les messages datent un peu). J'imagine donc qu'Apple à ajouté NSXMLDocument dans une récente mise à jour du SDK.
Le problème c'est que ça compile, mais ça ne me renvoit pas de NSXMLDocument...
Du coup je ne sais pas si ça vient du fichier qui n'est pas trouvé ou qui n'est pas correct, ou si ça vient de NSXMLDocument qui n'existe pas et qui renverrait nil par un pur miracle...

Je n'arrive pas à vérifier si mon fichier est trouvé ou non (je suis en train de chercher comment on lit un fichier, mais je suis un noob en Cocoa donc je perds un temps fou pour faire des trucs tous simples). En C ça me met des erreurs...

Si vous pouvez m'aider parce que vous avez déjà fait ce genre de chose, n'hésitez pas à me donner un tuyaux. Sinon inutile de chercher juste pour moi.

Merci beaucoup !

Je mets le morceau de code fourni par Apple et légèrement modifié par mes soins :

Bloc de code:
// Le chemin passé est : "Menus/menu1.xml"
- (NSXMLDocument *) createXMLDocumentFromFile:(NSString *)fichier { 
	NSXMLDocument *xmlDoc;
	NSError *err = nil;
        // test en retrouvant le HomeDirectory : 
	//NSString *path=[NSHomeDirectory() stringByAppendingPathComponent:fichier];
	//NSURL *furl = [NSURL fileURLWithPath:path];
	NSURL *furl = [[NSURL alloc] initWithString:fichier];
	if(!furl) {
		NSLog(@"Impossible de créer une URL à partir du fichier %@", fichier);
	}
	else { 
		NSLog(@"path = %@", [furl path]); // le NSURL est bien crée
		xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA) error:&err];
	} 
		
	if(xmlDoc == nil) { 
		// On réessaye avec l'option NSXMLDocumentTidyXML 
		xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl options:NSXMLDocumentTidyXML error:&err];
	} 
	if(xmlDoc == nil) { 
		if(err) {
			NSLog(@"Impossible de parser le fichier %@", fichier);
			//[self handleError:err];
		}
	}
	if(err) {
		//[self handleError:err];
	}
	return xmlDoc; 
}

Le log affiché est "Impossible de parser le fichier", et non pas "Impossible de créer l'URL".
J'ai essayé de mettre quelques points d'arrêts avec le déboggeur mais je n'ai rien trouvé qui pourrait m'aider.
Les 3 pistes que j'ai sont donc :
- le chemin du fichier n'est pas bon.
- le fichier n'est pas lisible par le parser.
- NSXMLDocument n'est toujours pas implémenté, mais c'est bizarre que ça ne fasse pas une erreur...

Encore merci pour votre aide !


EDIT : Il semblerait qu'avec une URL non locale (en http://...) ça fonctionne !! Je n'ai pas encore exploité l'arbre d'objet que ça m'a crée pour voir s'il a été crée correctement, mais je n'ai plus d'erreur !!
Il me reste 30 minutes pour finir mon parseur (au moins avec un fichier sur Internet).. la conférence de Nintendo est à 18h !!
Mais ça ne règle pas le problème de la lecture du fichier en local...
 
Dans le document 'iPhone OS for Cocoa Developers' présent sur le site dévelopeur Apple, tu peux lire ça au sujet du XML :

The NSXMLParser class is available in iPhone OS. You can use it to parse XML and extract information from an existing source of XML. However, NSXMLDocument and its associated classes (used to create, modify, and repeatedly query tree-based XML documents) are not available.
 
test en local fait un simple prog avec un main appel ta methode
faut savoir sortir d'xcode un peu

gcc xmldoc.m -framework Cocoa
Bloc de code:
#import <Cocoa/Cocoa.h>

@interface myDoc: NSObject 
{
}
- (NSXMLDocument *) createXMLDocumentFromFile:(NSString *)path;
@end

@implementation myDoc
// Le chemin passé est : "Menus/menu1.xml"
- (NSXMLDocument *) createXMLDocumentFromFile:(NSString *)path { 
    NSXMLDocument *xmlDoc;
    NSError *err = nil;
        // test en retrouvant le HomeDirectory : 
    //NSString *path=[NSHomeDirectory() stringByAppendingPathComponent:path];
    //NSURL *furl = [NSURL fileURLWithPath:path];
    NSURL *furl = [[NSURL alloc] initWithString:path];
    if(!furl) {
        NSLog(@"Impossible de créer une URL à partir du path %@", path);
    }
    else { 
        NSLog(@"path = %@", [furl path]); // le NSURL est bien crée
        xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA) error:&err];
    } 
        
    if(xmlDoc == nil) { 
        // On réessaye avec l'option NSXMLDocumentTidyXML 
        xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl options:NSXMLDocumentTidyXML error:&err];
    } 
    if(xmlDoc == nil) { 
        if(err) {
            NSLog(@"Impossible de parser le path %@", path);
            //[self handleError:err];
        }
    }
    if(err) {
        //[self handleError:err];
    }
    return xmlDoc; 
}
@end

int main(int argc, const char * argv[]) {
    if(argc == 2) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        
        myDoc *doc = [[myDoc alloc] init];
        
        [doc createXMLDocumentFromFile:
            [NSString stringWithCString : argv[1]]];
            
        [doc release];
        [pool drain];
          
        return 0;
    }
    
    return 1;
}
et en deux secondes tu trouves ta reponse, au lieu d'attendre sur le forum
un peu de methode et de reflexion svp, resultat une minute pour explorer toutes tes interogations

/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks

pour le support complet de certaines methodes ca depends des versions Iphone,
et apprend a utiliser nm(1) sur tes libraries targets pour tester la presence d'un symbol tu seras beaucoup
plus productif


et je ne vois pas ton probleme avec sax?
 
The NSXMLParser class is available in iPhone OS. You can use it to parse XML and extract information from an existing source of XML. However, NSXMLDocument and its associated classes (used to create, modify, and repeatedly query tree-based XML documents) are not available.
Je sais, mais ce qui est bizarre c'est que NSXMLDocument compile et marche sur le simulateur, mais ne compile pas sur le device. Ca m'embête vraiment de devoir faire un parser avec NSXMLParser parce que c'est plus chiant qu'avec NSXMLDocument pour le document que j'ai à parser. Du coup j'ai fait un parser en 30 minutes avec NSXMLDocument, puisque je n'utilise que le simulateur pour le moment. Ca m'a permis d'avancer un peu sur d'autres trucs.


J'ai un nouveau petit problème, mais cette fois ça concerne XCode :
Je testais mon programme, et j'ai mis pas mal de NSLog pour afficher quelques valeurs pendant l'exécution. Tout marchait bien depuis des lustres, jusqu'à aujourd'hui : lors d'une exécution, les logs ne se sont plus affichés. XCode a planté, et j'ai dû le forcer à quitter.
Depuis, dès que j'ouvre un projet dans XCode et que je clique sur Build and Go, il fait semblant de builder (mais il ne le fait pas), et lance l'application (compilée lorsqu'XCode marchait encore), sans afficher aucun log. Si en revanche je modifie le code, la compilation ne se termine jamais, et il est impossible de l'arrêter. Plusieurs fonctionnalités d'XCode ne répondent alors plus du tout, et je suis obligé de forcer à quitter...

Le même problème intervient quel que soit le projet que j'ouvre.
Je vais essayer de redémarrer mon Mac (on ne sait jamais).. Mais si ça ne change rien, est-ce que je devrais réinstaller XCode et le SDK de l'iPhone ?

Merci pour votre aide !

EDIT : Problème résolu : il suffisait de redémarrer le Mac ! Ma tentative de battre le record du monde de non redémarrage a encore échouée...
 
Salut !

Je suis désolé, mais j'ai encore un problème. Ca fait un bon moment que je cherche mais je ne vois pas trop comment faire.. J'ai beaucoup hésité avant de demander sur le forum parce que c'est dur et long à expliquer. Si vous avez la flemme de tout lire, lisez juste la phrase en gras, et si vous savez répondre ça pourra certainement m'aider.

Donc voilà mon problème :

Je dois respecter un MVC assez stricte.
Toutes mes vues sont des vues personnalisées et je ne fais rien dans IB.
J'ai une classe que j'appelle Manager qui doit récupérer les événements de l'écran. Pour cela, dans ma hiérarchie de vues, j'implémente nextResponder et je transmets la superview, jusqu'à la vue principale (qui contient toutes les autres), qui elle transmet les événements à mon Manager (qui hérite de UIResponder), dans lequel j'implémente les méthodes pour récupérer les événements.
Jusque là tout se passe bien et ça marche.

Dans son interaction, l'utilisateur peut maintenir son doigt sur l'écran, et le faire glisser, ce qui provoque une action dès que le doigt dépasse un certain endroit. Cette action implique la suppression de certaines vues dans la hiérarchie de vues, et leur remplacement par d'autres vues.
Le problème c'est que dès lors qu'il y a eu cette suppression de vue (avec des removeFromSuperview), le Manager arrête de recevoir les événements "moved", et le "glissé" est stoppé net. Mais si je retape sur l'écran, alors ça remarche normalement.

Je ne comprends pas totalement ce qui se passe, mais j'ai trouvé quelques pistes.
- D'après mes expérimentations, le problème vient bien de la suppression des vues. J'ai l'impression que des événements passent dans les vues les plus basses de la hiérarchie, et y sont encore lorsque je coupe la liaison avec le haut de la hiérarchie, ce qui fait perdre ces événements. Pour parler plus techniquement, ça fait perdre tout le UIEvent (il n'est plus transmis au Manager), et c'est certainement pour cela qu'il faut que je relâche ma souris pour que je récupère la réception des événements.
- Si j'affiche quelque chose dans la méthode nextResponder de ma vue de plus bas niveau, je vois ces logs s'afficher dès que le Manager perds la main (juste après la suppression de vues).

Bref, d'après ces constatations, je pense que la solution serait de sortir totalement ces vues potentiellement supprimables de la Responder Chain, pour que les événements ne passent jamais par elles. Mais je n'ai aucune idée de comment m'y prendre..

Une autre solution serait que la méthode nextReponder de ma vue de plus bas niveau retourne le Manager. Mais cette vue ne doit pas connaître le Manager pour préserver l'indépendance entre la vue et le contrôleur...

Un peu d'aide ne serait pas de refus ^^.

Merci beaucoup !!
 
Sur iPhone cette méthode semble s'appeler becomeFirstResponder.
Malheureusement j'ai déjà essayé et ça ne fonctionne pas.. Mon Manager perd toujours la main dès que je supprime mes vues... Et un NSLog dans la méthode becomeFirstResponder m'indique qu'elle n'est jamais appelée.
Mais par contre les événement arrivent bien au Manager tant que je ne supprime pas mes vues. Et pourtant j'ai vraiment besoin de supprimer ces vues...

Je ne sais vraiment plus quoi faire..

Merci pour ton aide en tout cas !
 
Sur iPhone cette méthode semble s'appeler becomeFirstResponder.
Et un NSLog dans la méthode becomeFirstResponder m'indique qu'elle n'est jamais appelée.

En fait, on se fout de tout ça: la responder chain est utilisée pour les événements de type clavier, pas pour ceux de la souris (euh, du doigt) ! Le first responder est la vue qui détient le "focus".


A relire ton dernier message, ce qui se passe est que le mouseDragged est interrompu, ce qui est normal, puisque ta vue disparaît. La question est donc: comment faire pour qu'une vue n'intercepte pas les événements de type souris ? Et là, à voir la doc d'Apple, je ne lis aucune solution. Peut-être peux-tu bidouiller les méthodes mouseDown: mouseUp: et mouseDragged: de tes subviews:

Bloc de code:
- (void)mouseDragged:(NSEvent *)theEvent
{
    [superview mouseDragged:theEvent];
}

Dans ce cas, il ne faut pas que tu retires la subview, mais que tu la masques.


Bonne chance, mais c'est ce que je t'écrivais au tout début: Il est très simple de faire avec Cocoa les choses standard. C'est un gros avantage (les applis se comportent toutes de la même façon), mais je conçois que l'iPhone ne se programme pas comme un Mac.
 
Merci beaucoup pour ton aide Céroce !

J'ai décidé de recommencer toute la partie interaction de mon application, parce que j'avais d'autres problèmes aussi avec la suppression de mes vues et les animations. Donc j'ai respécifié en tenant compte de ces contraintes et je recommence d'une autre manière en espérant que cette fois-ci ça ne coince pas.
Ce qui me choque avec le problème que j'évoquais dans mon précédent message c'est que je n'implémente pas les méthodes touchesBegan, touchesMoved et touchesEnded dans les vues que je supprime. Et je ne veux pas le faire afin de bien séparer la vue du contrôle. Donc dès la window je transmets les événements à la partie contrôle sans passer par ces trois fonctions.

Je connaissais vaguement le forum objective-cocoa, mais je n'avais jamais pris le temps de m'inscrire. Je me suis inscrit, et je pense en effet que ce sera plus simple de trouver des réponses dans ce forum spécialisé que sur MacGé en plein mois d'août ^^.

Encore merci !!
 
Salut à tous,

J'ai aussi pas mal de probleme avec les iViewController.
Si je crée une vue depuis un Nib, puis que je fait tourner le simulateur d'iPhone c'est ok.
Mais si je fait d'abord tourner le simulateur et crée la vue ensuite, la rotation n'est pas prise en compte.

Cordialement
 
Bonjour Dr-cube, je pense au vue de ton topic que tu pourrais peut être m'aider pour un travail, puis je te contacter par mp éventuellement ?
 
Salut à tous,

J'ai aussi pas mal de probleme avec les iViewController.
Si je crée une vue depuis un Nib, puis que je fait tourner le simulateur d'iPhone c'est ok.
Mais si je fait d'abord tourner le simulateur et crée la vue ensuite, la rotation n'est pas prise en compte.

Cordialement

oui et specialement les tab views le autoresize subviews est une catastrophe, et j'ai eu le meme probleme que toi et c'est pareil pour la rotation redefinie l'appel dans la subvue pour voir et bien sur nslog :D
 
oui et specialement les tab views le autoresize subviews est une catastrophe, et j'ai eu le meme probleme que toi et c'est pareil pour la rotation redefinie l'appel dans la subvue pour voir et bien sur nslog :D

Merci Tatouille, mais mon message datait d'Aout...
J'ai solutionné le problème depuis mais ne me demande pas comment, j'ai oublié...

Cordialement